Delete any rest of old public share/unshare flow
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / FileDisplayActivity.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author Bartek Przybylski
5 * @author David A. Velasco
6 * Copyright (C) 2011 Bartek Przybylski
7 * Copyright (C) 2015 ownCloud Inc.
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2,
11 * as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 package com.owncloud.android.ui.activity;
24
25 import android.accounts.Account;
26 import android.accounts.AccountManager;
27 import android.accounts.AuthenticatorException;
28 import android.annotation.TargetApi;
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.ContentResolver;
32 import android.content.Context;
33 import android.content.DialogInterface;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.ServiceConnection;
37 import android.content.SharedPreferences;
38 import android.content.SyncRequest;
39 import android.content.res.Resources.NotFoundException;
40 import android.database.Cursor;
41 import android.net.Uri;
42 import android.os.Build;
43 import android.os.Bundle;
44 import android.os.IBinder;
45 import android.preference.PreferenceManager;
46 import android.provider.OpenableColumns;
47 import android.support.v4.app.Fragment;
48 import android.support.v4.app.FragmentManager;
49 import android.support.v4.app.FragmentTransaction;
50 import android.support.v4.content.ContextCompat;
51 import android.support.v4.view.GravityCompat;
52 import android.support.v7.app.AlertDialog;
53 import android.view.Menu;
54 import android.view.MenuInflater;
55 import android.view.MenuItem;
56 import android.view.View;
57 import android.widget.ProgressBar;
58 import android.widget.RelativeLayout;
59 import android.widget.TextView;
60 import android.widget.Toast;
61
62 import com.owncloud.android.MainApp;
63 import com.owncloud.android.R;
64 import com.owncloud.android.datamodel.FileDataStorageManager;
65 import com.owncloud.android.datamodel.OCFile;
66 import com.owncloud.android.files.services.FileDownloader;
67 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
68 import com.owncloud.android.files.services.FileUploader;
69 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
70 import com.owncloud.android.lib.common.OwnCloudAccount;
71 import com.owncloud.android.lib.common.OwnCloudClient;
72 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
73 import com.owncloud.android.lib.common.OwnCloudCredentials;
74 import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
75 import com.owncloud.android.lib.common.network.CertificateCombinedException;
76 import com.owncloud.android.lib.common.operations.RemoteOperation;
77 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
78 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
79 import com.owncloud.android.lib.common.utils.Log_OC;
80 import com.owncloud.android.operations.CopyFileOperation;
81 import com.owncloud.android.operations.CreateFolderOperation;
82 import com.owncloud.android.operations.MoveFileOperation;
83 import com.owncloud.android.operations.RefreshFolderOperation;
84 import com.owncloud.android.operations.RemoveFileOperation;
85 import com.owncloud.android.operations.RenameFileOperation;
86 import com.owncloud.android.operations.SynchronizeFileOperation;
87 import com.owncloud.android.services.observer.FileObserverService;
88 import com.owncloud.android.syncadapter.FileSyncAdapter;
89 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
90 import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
91 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
92 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
93 import com.owncloud.android.ui.dialog.UploadSourceDialogFragment;
94 import com.owncloud.android.ui.fragment.FileDetailFragment;
95 import com.owncloud.android.ui.fragment.FileFragment;
96 import com.owncloud.android.ui.fragment.OCFileListFragment;
97 import com.owncloud.android.ui.preview.PreviewImageActivity;
98 import com.owncloud.android.ui.preview.PreviewImageFragment;
99 import com.owncloud.android.ui.preview.PreviewMediaFragment;
100 import com.owncloud.android.ui.preview.PreviewTextFragment;
101 import com.owncloud.android.ui.preview.PreviewVideoActivity;
102 import com.owncloud.android.utils.DisplayUtils;
103 import com.owncloud.android.utils.ErrorMessageAdapter;
104 import com.owncloud.android.utils.FileStorageUtils;
105 import com.owncloud.android.utils.UriUtils;
106
107 import java.io.File;
108
109 /**
110 * Displays, what files the user has available in his ownCloud.
111 */
112
113 public class FileDisplayActivity extends HookActivity
114 implements FileFragment.ContainerActivity,
115 OnSslUntrustedCertListener, OnEnforceableRefreshListener {
116
117 private SyncBroadcastReceiver mSyncBroadcastReceiver;
118 private UploadFinishReceiver mUploadFinishReceiver;
119 private DownloadFinishReceiver mDownloadFinishReceiver;
120 private RemoteOperationResult mLastSslUntrustedServerResult = null;
121
122 private boolean mDualPane;
123 private View mLeftFragmentContainer;
124 private View mRightFragmentContainer;
125 private ProgressBar mProgressBar;
126
127 private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
128 private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
129 private static final String KEY_WAITING_TO_SEND = "WAITING_TO_SEND";
130
131 public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
132
133 public static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
134 public static final int ACTION_SELECT_MULTIPLE_FILES = 2;
135 public static final int ACTION_MOVE_FILES = 3;
136 public static final int ACTION_COPY_FILES = 4;
137
138 private static final String TAG = FileDisplayActivity.class.getSimpleName();
139
140 private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
141 private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
142
143 private OCFile mWaitingToPreview;
144
145 private boolean mSyncInProgress = false;
146
147 private static String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
148 private static String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
149 private static String DIALOG_UPLOAD_SOURCE = "DIALOG_UPLOAD_SOURCE";
150 private static String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED";
151
152 private OCFile mWaitingToSend;
153
154
155 @Override
156 protected void onCreate(Bundle savedInstanceState) {
157 Log_OC.v(TAG, "onCreate() start");
158
159 super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account
160 // is valid
161
162 /// grant that FileObserverService is watching favorite files
163 if (savedInstanceState == null) {
164 Intent initObserversIntent = FileObserverService.makeInitIntent(this);
165 startService(initObserversIntent);
166 }
167
168 /// Load of saved instance state
169 if(savedInstanceState != null) {
170 mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(
171 FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
172 mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS);
173 mWaitingToSend = (OCFile) savedInstanceState.getParcelable(
174 FileDisplayActivity.KEY_WAITING_TO_SEND);
175 } else {
176 mWaitingToPreview = null;
177 mSyncInProgress = false;
178 mWaitingToSend = null;
179 }
180
181 /// USER INTERFACE
182
183 // Inflate and set the layout view
184 setContentView(R.layout.files);
185
186 // Navigation Drawer
187 initDrawer();
188
189 mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
190 mProgressBar.setIndeterminateDrawable(
191 ContextCompat.getDrawable(this,
192 R.drawable.actionbar_progress_indeterminate_horizontal));
193
194 mDualPane = getResources().getBoolean(R.bool.large_land_layout);
195 mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
196 mRightFragmentContainer = findViewById(R.id.right_fragment_container);
197 if (savedInstanceState == null) {
198 createMinFragments();
199 }
200
201 // Action bar setup
202 getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS,
203 // according to the official
204 // documentation
205
206 // enable ActionBar app icon to behave as action to toggle nav drawer
207 //getSupportActionBar().setDisplayHomeAsUpEnabled(true);
208 getSupportActionBar().setHomeButtonEnabled(true);
209
210 mProgressBar.setIndeterminate(mSyncInProgress);
211 // always AFTER setContentView(...) ; to work around bug in its implementation
212
213 setBackgroundText();
214
215 Log_OC.v(TAG, "onCreate() end");
216 }
217
218 @Override
219 protected void onStart() {
220 Log_OC.v(TAG, "onStart() start");
221 super.onStart();
222 Log_OC.v(TAG, "onStart() end");
223 }
224
225 @Override
226 protected void onStop() {
227 Log_OC.v(TAG, "onStop() start");
228 super.onStop();
229 Log_OC.v(TAG, "onStop() end");
230 }
231
232 @Override
233 protected void onDestroy() {
234 Log_OC.v(TAG, "onDestroy() start");
235 super.onDestroy();
236 Log_OC.v(TAG, "onDestroy() end");
237 }
238
239 /**
240 * Called when the ownCloud {@link Account} associated to the Activity was just updated.
241 */
242 @Override
243 protected void onAccountSet(boolean stateWasRecovered) {
244 super.onAccountSet(stateWasRecovered);
245 if (getAccount() != null) {
246 /// Check whether the 'main' OCFile handled by the Activity is contained in the
247 // current Account
248 OCFile file = getFile();
249 // get parent from path
250 String parentPath = "";
251 if (file != null) {
252 if (file.isDown() && file.getLastSyncDateForProperties() == 0) {
253 // upload in progress - right now, files are not inserted in the local
254 // cache until the upload is successful get parent from path
255 parentPath = file.getRemotePath().substring(0,
256 file.getRemotePath().lastIndexOf(file.getFileName()));
257 if (getStorageManager().getFileByPath(parentPath) == null)
258 file = null; // not able to know the directory where the file is uploading
259 } else {
260 file = getStorageManager().getFileByPath(file.getRemotePath());
261 // currentDir = null if not in the current Account
262 }
263 }
264 if (file == null) {
265 // fall back to root folder
266 file = getStorageManager().getFileByPath(OCFile.ROOT_PATH); // never returns null
267 }
268 setFile(file);
269
270 if (mAccountWasSet) {
271 RelativeLayout navigationDrawerLayout = (RelativeLayout) findViewById(R.id.left_drawer);
272 if (navigationDrawerLayout != null && getAccount() != null) {
273 TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
274 int lastAtPos = getAccount().name.lastIndexOf("@");
275 username.setText(getAccount().name.substring(0, lastAtPos));
276 }
277 }
278
279 if (!stateWasRecovered) {
280 Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..");
281 initFragmentsWithFile();
282 if (file.isFolder()) {
283 startSyncFolderOperation(file, false);
284 }
285
286 } else {
287 updateFragmentsVisibility(!file.isFolder());
288 updateActionBarTitleAndHomeButton(file.isFolder() ? null : file);
289 }
290 }
291 }
292
293 private void createMinFragments() {
294 OCFileListFragment listOfFiles = new OCFileListFragment();
295 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
296 transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
297 transaction.commit();
298 }
299
300 private void initFragmentsWithFile() {
301 if (getAccount() != null && getFile() != null) {
302 /// First fragment
303 OCFileListFragment listOfFiles = getListOfFilesFragment();
304 if (listOfFiles != null) {
305 listOfFiles.listDirectory(getCurrentDir());
306 // TODO Enable when "On Device" is recovered
307 // listOfFiles.listDirectory(getCurrentDir(), MainApp.getOnlyOnDevice());
308
309 } else {
310 Log_OC.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
311 }
312
313 /// Second fragment
314 OCFile file = getFile();
315 Fragment secondFragment = chooseInitialSecondFragment(file);
316 if (secondFragment != null) {
317 setSecondFragment(secondFragment);
318 updateFragmentsVisibility(true);
319 updateActionBarTitleAndHomeButton(file);
320
321 } else {
322 cleanSecondFragment();
323 if (file.isDown() && PreviewTextFragment.canBePreviewed(file))
324 startTextPreview(file);
325 }
326
327 } else {
328 Log_OC.wtf(TAG, "initFragments() called with invalid NULLs!");
329 if (getAccount() == null) {
330 Log_OC.wtf(TAG, "\t account is NULL");
331 }
332 if (getFile() == null) {
333 Log_OC.wtf(TAG, "\t file is NULL");
334 }
335 }
336 }
337
338 private Fragment chooseInitialSecondFragment(OCFile file) {
339 Fragment secondFragment = null;
340 if (file != null && !file.isFolder()) {
341 if (file.isDown() && PreviewMediaFragment.canBePreviewed(file)
342 && file.getLastSyncDateForProperties() > 0 // temporal fix
343 ) {
344 int startPlaybackPosition =
345 getIntent().getIntExtra(PreviewVideoActivity.EXTRA_START_POSITION, 0);
346 boolean autoplay =
347 getIntent().getBooleanExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, true);
348 secondFragment = new PreviewMediaFragment(file, getAccount(),
349 startPlaybackPosition, autoplay);
350
351 } else if (file.isDown() && PreviewTextFragment.canBePreviewed(file)) {
352 secondFragment = null;
353 } else {
354 secondFragment = FileDetailFragment.newInstance(file, getAccount());
355 }
356 }
357 return secondFragment;
358 }
359
360
361 /**
362 * Replaces the second fragment managed by the activity with the received as
363 * a parameter.
364 * <p/>
365 * Assumes never will be more than two fragments managed at the same time.
366 *
367 * @param fragment New second Fragment to set.
368 */
369 private void setSecondFragment(Fragment fragment) {
370 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
371 transaction.replace(R.id.right_fragment_container, fragment, TAG_SECOND_FRAGMENT);
372 transaction.commit();
373 }
374
375
376 private void updateFragmentsVisibility(boolean existsSecondFragment) {
377 if (mDualPane) {
378 if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
379 mLeftFragmentContainer.setVisibility(View.VISIBLE);
380 }
381 if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
382 mRightFragmentContainer.setVisibility(View.VISIBLE);
383 }
384
385 } else if (existsSecondFragment) {
386 if (mLeftFragmentContainer.getVisibility() != View.GONE) {
387 mLeftFragmentContainer.setVisibility(View.GONE);
388 }
389 if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
390 mRightFragmentContainer.setVisibility(View.VISIBLE);
391 }
392
393 } else {
394 if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
395 mLeftFragmentContainer.setVisibility(View.VISIBLE);
396 }
397 if (mRightFragmentContainer.getVisibility() != View.GONE) {
398 mRightFragmentContainer.setVisibility(View.GONE);
399 }
400 }
401 }
402
403
404 private OCFileListFragment getListOfFilesFragment() {
405 Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(
406 FileDisplayActivity.TAG_LIST_OF_FILES);
407 if (listOfFiles != null) {
408 return (OCFileListFragment) listOfFiles;
409 }
410 Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
411 return null;
412 }
413
414 public FileFragment getSecondFragment() {
415 Fragment second = getSupportFragmentManager().findFragmentByTag(
416 FileDisplayActivity.TAG_SECOND_FRAGMENT);
417 if (second != null) {
418 return (FileFragment) second;
419 }
420 return null;
421 }
422
423 protected void cleanSecondFragment() {
424 Fragment second = getSecondFragment();
425 if (second != null) {
426 FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
427 tr.remove(second);
428 tr.commit();
429 }
430 updateFragmentsVisibility(false);
431 updateActionBarTitleAndHomeButton(null);
432 }
433
434 protected void refreshListOfFilesFragment() {
435 OCFileListFragment fileListFragment = getListOfFilesFragment();
436 if (fileListFragment != null) {
437 fileListFragment.listDirectory();
438 // TODO Enable when "On Device" is recovered ?
439 // fileListFragment.listDirectory(MainApp.getOnlyOnDevice());
440 }
441 }
442
443 protected void refreshSecondFragment(String downloadEvent, String downloadedRemotePath,
444 boolean success) {
445 FileFragment secondFragment = getSecondFragment();
446 boolean waitedPreview = (mWaitingToPreview != null &&
447 mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
448 if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
449 FileDetailFragment detailsFragment = (FileDetailFragment) secondFragment;
450 OCFile fileInFragment = detailsFragment.getFile();
451 if (fileInFragment != null &&
452 !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
453 // the user browsed to other file ; forget the automatic preview
454 mWaitingToPreview = null;
455
456 } else if (downloadEvent.equals(FileDownloader.getDownloadAddedMessage())) {
457 // grant that the right panel updates the progress bar
458 detailsFragment.listenForTransferProgress();
459 detailsFragment.updateFileDetails(true, false);
460
461 } else if (downloadEvent.equals(FileDownloader.getDownloadFinishMessage())) {
462 // update the right panel
463 boolean detailsFragmentChanged = false;
464 if (waitedPreview) {
465 if (success) {
466 mWaitingToPreview = getStorageManager().getFileById(
467 mWaitingToPreview.getFileId()); // update the file from database,
468 // for the local storage path
469 if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
470 startMediaPreview(mWaitingToPreview, 0, true);
471 detailsFragmentChanged = true;
472 } else if (PreviewTextFragment.canBePreviewed(mWaitingToPreview)) {
473 startTextPreview(mWaitingToPreview);
474 detailsFragmentChanged = true;
475 } else {
476 getFileOperationsHelper().openFile(mWaitingToPreview);
477 }
478 }
479 mWaitingToPreview = null;
480 }
481 if (!detailsFragmentChanged) {
482 detailsFragment.updateFileDetails(false, (success));
483 }
484 }
485 }
486 }
487
488 @Override
489 public boolean onPrepareOptionsMenu(Menu menu) {
490 boolean drawerOpen = mDrawerLayout.isDrawerOpen(GravityCompat.START);
491 menu.findItem(R.id.action_upload).setVisible(!drawerOpen);
492 menu.findItem(R.id.action_create_dir).setVisible(!drawerOpen);
493 menu.findItem(R.id.action_sort).setVisible(!drawerOpen);
494 menu.findItem(R.id.action_sync_account).setVisible(!drawerOpen);
495
496 return super.onPrepareOptionsMenu(menu);
497 }
498
499 @Override
500 public boolean onCreateOptionsMenu(Menu menu) {
501 MenuInflater inflater = getMenuInflater();
502 inflater.inflate(R.menu.main_menu, menu);
503 return true;
504 }
505
506
507 @Override
508 public boolean onOptionsItemSelected(MenuItem item) {
509 boolean retval = true;
510 switch (item.getItemId()) {
511 case R.id.action_create_dir: {
512 CreateFolderDialogFragment dialog =
513 CreateFolderDialogFragment.newInstance(getCurrentDir());
514 dialog.show(getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
515 break;
516 }
517
518 case R.id.action_sync_account: {
519 startSynchronization();
520 break;
521 }
522 case R.id.action_upload: {
523 UploadSourceDialogFragment dialog =
524 UploadSourceDialogFragment.newInstance(getAccount());
525 dialog.show(getSupportFragmentManager(), DIALOG_UPLOAD_SOURCE);
526 break;
527 }
528 case android.R.id.home: {
529 FileFragment second = getSecondFragment();
530 OCFile currentDir = getCurrentDir();
531 if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
532 mDrawerLayout.closeDrawer(GravityCompat.START);
533 } else if((currentDir != null && currentDir.getParentId() != 0) ||
534 (second != null && second.getFile() != null)) {
535 onBackPressed();
536
537 } else {
538 mDrawerLayout.openDrawer(GravityCompat.START);
539 }
540 break;
541 }
542 case R.id.action_sort: {
543 SharedPreferences appPreferences = PreferenceManager
544 .getDefaultSharedPreferences(this);
545
546 // Read sorting order, default to sort by name ascending
547 Integer sortOrder = appPreferences
548 .getInt("sortOrder", FileStorageUtils.SORT_NAME);
549
550 AlertDialog.Builder builder = new AlertDialog.Builder(this);
551 builder.setTitle(R.string.actionbar_sort_title)
552 .setSingleChoiceItems(R.array.actionbar_sortby, sortOrder ,
553 new DialogInterface.OnClickListener() {
554 public void onClick(DialogInterface dialog, int which) {
555 switch (which){
556 case 0:
557 sortByName(true);
558 break;
559 case 1:
560 sortByDate(false);
561 break;
562 }
563
564 dialog.dismiss();
565 }
566 });
567 builder.create().show();
568 break;
569 }
570 default:
571 retval = super.onOptionsItemSelected(item);
572 }
573 return retval;
574 }
575
576 private void startSynchronization() {
577 Log_OC.d(TAG, "Got to start sync");
578 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
579 Log_OC.d(TAG, "Canceling all syncs for " + MainApp.getAuthority());
580 ContentResolver.cancelSync(null, MainApp.getAuthority());
581 // cancel the current synchronizations of any ownCloud account
582 Bundle bundle = new Bundle();
583 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
584 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
585 Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " +
586 MainApp.getAuthority());
587 ContentResolver.requestSync(
588 getAccount(),
589 MainApp.getAuthority(), bundle);
590 } else {
591 Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " +
592 MainApp.getAuthority() + " with new API");
593 SyncRequest.Builder builder = new SyncRequest.Builder();
594 builder.setSyncAdapter(getAccount(), MainApp.getAuthority());
595 builder.setExpedited(true);
596 builder.setManual(true);
597 builder.syncOnce();
598
599 // Fix bug in Android Lollipop when you click on refresh the whole account
600 Bundle extras = new Bundle();
601 builder.setExtras(extras);
602
603 SyncRequest request = builder.build();
604 ContentResolver.requestSync(request);
605 }
606 }
607
608 /**
609 * Called, when the user selected something for uploading
610 *
611 */
612 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
613 @Override
614 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
615
616 if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK ||
617 resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
618 //getClipData is only supported on api level 16+, Jelly Bean
619 if (data.getData() == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
620 for( int i = 0; i < data.getClipData().getItemCount(); i++){
621 Intent intent = new Intent();
622 intent.setData(data.getClipData().getItemAt(i).getUri());
623 requestSimpleUpload(intent, resultCode);
624 }
625 }else {
626 requestSimpleUpload(data, resultCode);
627 }
628 } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK ||
629 resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
630 requestMultipleUpload(data, resultCode);
631
632 } else if (requestCode == ACTION_MOVE_FILES && resultCode == RESULT_OK){
633 final Intent fData = data;
634 final int fResultCode = resultCode;
635 getHandler().postDelayed(
636 new Runnable() {
637 @Override
638 public void run() {
639 requestMoveOperation(fData, fResultCode);
640 }
641 },
642 DELAY_TO_REQUEST_OPERATIONS_LATER
643 );
644
645 } else if (requestCode == ACTION_COPY_FILES && resultCode == RESULT_OK) {
646
647 final Intent fData = data;
648 final int fResultCode = resultCode;
649 getHandler().postDelayed(
650 new Runnable() {
651 @Override
652 public void run() {
653 requestCopyOperation(fData, fResultCode);
654 }
655 },
656 DELAY_TO_REQUEST_OPERATIONS_LATER
657 );
658
659 } else {
660 super.onActivityResult(requestCode, resultCode, data);
661 }
662
663 }
664
665 private void requestMultipleUpload(Intent data, int resultCode) {
666 String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
667 if (filePaths != null) {
668 String[] remotePaths = new String[filePaths.length];
669 String remotePathBase = getCurrentDir().getRemotePath();
670 for (int j = 0; j< remotePaths.length; j++) {
671 remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
672 }
673
674 Intent i = new Intent(this, FileUploader.class);
675 i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
676 i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
677 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
678 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
679 if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
680 i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
681 startService(i);
682
683 } else {
684 Log_OC.d(TAG, "User clicked on 'Update' with no selection");
685 Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected),
686 Toast.LENGTH_LONG);
687 t.show();
688 return;
689 }
690 }
691
692
693 private void requestSimpleUpload(Intent data, int resultCode) {
694 String filePath = null;
695 String mimeType = null;
696
697 Uri selectedImageUri = data.getData();
698
699 try {
700 mimeType = getContentResolver().getType(selectedImageUri);
701
702 String fileManagerString = selectedImageUri.getPath();
703 String selectedImagePath = UriUtils.getLocalPath(selectedImageUri, this);
704
705 if (selectedImagePath != null)
706 filePath = selectedImagePath;
707 else
708 filePath = fileManagerString;
709
710 } catch (Exception e) {
711 Log_OC.e(TAG, "Unexpected exception when trying to read the result of " +
712 "Intent.ACTION_GET_CONTENT", e);
713
714 } finally {
715 if (filePath == null) {
716 Log_OC.e(TAG, "Couldn't resolve path to file");
717 Toast t = Toast.makeText(
718 this, getString(R.string.filedisplay_unexpected_bad_get_content),
719 Toast.LENGTH_LONG
720 );
721 t.show();
722 return;
723 }
724 }
725
726 Intent i = new Intent(this, FileUploader.class);
727 i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
728 OCFile currentDir = getCurrentDir();
729 String remotePath = (currentDir != null) ? currentDir.getRemotePath() : OCFile.ROOT_PATH;
730
731 if (filePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
732 Cursor cursor = getContentResolver().query(Uri.parse(filePath), null, null, null, null);
733 try {
734 if (cursor != null && cursor.moveToFirst()) {
735 String displayName = cursor.getString(cursor.getColumnIndex(
736 OpenableColumns.DISPLAY_NAME));
737 Log_OC.v(TAG, "Display Name: " + displayName );
738
739 displayName.replace(File.separatorChar, '_');
740 displayName.replace(File.pathSeparatorChar, '_');
741 remotePath += displayName + DisplayUtils.getComposedFileExtension(filePath);
742
743 }
744 // and what happens in case of error?; wrong target name for the upload
745 } finally {
746 cursor.close();
747 }
748
749 } else {
750 remotePath += new File(filePath).getName();
751 }
752
753 i.putExtra(FileUploader.KEY_LOCAL_FILE, filePath);
754 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePath);
755 i.putExtra(FileUploader.KEY_MIME_TYPE, mimeType);
756 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
757 if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
758 i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
759 startService(i);
760 }
761
762 /**
763 * Request the operation for moving the file/folder from one path to another
764 *
765 * @param data Intent received
766 * @param resultCode Result code received
767 */
768 private void requestMoveOperation(Intent data, int resultCode) {
769 OCFile folderToMoveAt = (OCFile) data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
770 OCFile targetFile = (OCFile) data.getParcelableExtra(FolderPickerActivity.EXTRA_FILE);
771 getFileOperationsHelper().moveFile(folderToMoveAt, targetFile);
772 }
773
774 /**
775 * Request the operation for copying the file/folder from one path to another
776 *
777 * @param data Intent received
778 * @param resultCode Result code received
779 */
780 private void requestCopyOperation(Intent data, int resultCode) {
781 OCFile folderToMoveAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
782 OCFile targetFile = data.getParcelableExtra(FolderPickerActivity.EXTRA_FILE);
783 getFileOperationsHelper().copyFile(folderToMoveAt, targetFile);
784 }
785
786 @Override
787 public void onBackPressed() {
788 if (!isDrawerOpen()){
789 OCFileListFragment listOfFiles = getListOfFilesFragment();
790 if (mDualPane || getSecondFragment() == null) {
791 OCFile currentDir = getCurrentDir();
792 if (currentDir == null || currentDir.getParentId() == FileDataStorageManager.ROOT_PARENT_ID) {
793 finish();
794 return;
795 }
796 if (listOfFiles != null) { // should never be null, indeed
797 listOfFiles.onBrowseUp();
798 }
799 }
800 if (listOfFiles != null) { // should never be null, indeed
801 setFile(listOfFiles.getCurrentFile());
802 }
803 cleanSecondFragment();
804 } else {
805 super.onBackPressed();
806 }
807 }
808
809 @Override
810 protected void onSaveInstanceState(Bundle outState) {
811 // responsibility of restore is preferred in onCreate() before than in
812 // onRestoreInstanceState when there are Fragments involved
813 Log_OC.v(TAG, "onSaveInstanceState() start");
814 super.onSaveInstanceState(outState);
815 outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
816 outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
817 //outState.putBoolean(FileDisplayActivity.KEY_REFRESH_SHARES_IN_PROGRESS,
818 // mRefreshSharesInProgress);
819 outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_SEND, mWaitingToSend);
820
821 Log_OC.v(TAG, "onSaveInstanceState() end");
822 }
823
824
825 @Override
826 protected void onResume() {
827 Log_OC.v(TAG, "onResume() start");
828 super.onResume();
829 // refresh Navigation Drawer account list
830 mNavigationDrawerAdapter.updateAccountList();
831
832 // refresh list of files
833 refreshListOfFilesFragment();
834
835 // Listen for sync messages
836 IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
837 syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
838 syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
839 syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
840 syncIntentFilter.addAction(RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
841 mSyncBroadcastReceiver = new SyncBroadcastReceiver();
842 registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
843 //LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadcastReceiver,
844 // syncIntentFilter);
845
846 // Listen for upload messages
847 IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.getUploadFinishMessage());
848 mUploadFinishReceiver = new UploadFinishReceiver();
849 registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
850
851 // Listen for download messages
852 IntentFilter downloadIntentFilter = new IntentFilter(
853 FileDownloader.getDownloadAddedMessage());
854 downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage());
855 mDownloadFinishReceiver = new DownloadFinishReceiver();
856 registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
857
858 Log_OC.v(TAG, "onResume() end");
859
860 }
861
862
863 @Override
864 protected void onPause() {
865 Log_OC.v(TAG, "onPause() start");
866 if (mSyncBroadcastReceiver != null) {
867 unregisterReceiver(mSyncBroadcastReceiver);
868 //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
869 mSyncBroadcastReceiver = null;
870 }
871 if (mUploadFinishReceiver != null) {
872 unregisterReceiver(mUploadFinishReceiver);
873 mUploadFinishReceiver = null;
874 }
875 if (mDownloadFinishReceiver != null) {
876 unregisterReceiver(mDownloadFinishReceiver);
877 mDownloadFinishReceiver = null;
878 }
879
880 super.onPause();
881 Log_OC.v(TAG, "onPause() end");
882 }
883
884
885 private class SyncBroadcastReceiver extends BroadcastReceiver {
886
887 /**
888 * {@link BroadcastReceiver} to enable syncing feedback in UI
889 */
890 @Override
891 public void onReceive(Context context, Intent intent) {
892 try {
893 String event = intent.getAction();
894 Log_OC.d(TAG, "Received broadcast " + event);
895 String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME);
896 String synchFolderRemotePath =
897 intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH);
898 RemoteOperationResult synchResult =
899 (RemoteOperationResult)intent.getSerializableExtra(
900 FileSyncAdapter.EXTRA_RESULT);
901 boolean sameAccount = (getAccount() != null &&
902 accountName.equals(getAccount().name) && getStorageManager() != null);
903
904 if (sameAccount) {
905
906 if (FileSyncAdapter.EVENT_FULL_SYNC_START.equals(event)) {
907 mSyncInProgress = true;
908
909 } else {
910 OCFile currentFile = (getFile() == null) ? null :
911 getStorageManager().getFileByPath(getFile().getRemotePath());
912 OCFile currentDir = (getCurrentDir() == null) ? null :
913 getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
914
915 if (currentDir == null) {
916 // current folder was removed from the server
917 Toast.makeText( FileDisplayActivity.this,
918 String.format(
919 getString(R.string.
920 sync_current_folder_was_removed),
921 synchFolderRemotePath),
922 Toast.LENGTH_LONG)
923 .show();
924
925 browseToRoot();
926
927 } else {
928 if (currentFile == null && !getFile().isFolder()) {
929 // currently selected file was removed in the server, and now we
930 // know it
931 cleanSecondFragment();
932 currentFile = currentDir;
933 }
934
935 if (synchFolderRemotePath != null &&
936 currentDir.getRemotePath().equals(synchFolderRemotePath)) {
937 OCFileListFragment fileListFragment = getListOfFilesFragment();
938 if (fileListFragment != null) {
939 fileListFragment.listDirectory();
940 // TODO Enable when "On Device" is recovered ?
941 // fileListFragment.listDirectory(currentDir,
942 // MainApp.getOnlyOnDevice());
943 }
944 }
945 setFile(currentFile);
946 }
947
948 mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) &&
949 !RefreshFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED
950 .equals(event));
951
952 if (RefreshFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED.
953 equals(event) &&/// TODO refactor and make common
954
955 synchResult != null && !synchResult.isSuccess() &&
956 (synchResult.getCode() == ResultCode.UNAUTHORIZED ||
957 synchResult.isIdPRedirection() ||
958 (synchResult.isException() && synchResult.getException()
959 instanceof AuthenticatorException))) {
960
961
962 try {
963 OwnCloudClient client;
964 OwnCloudAccount ocAccount =
965 new OwnCloudAccount(getAccount(), context);
966 client = (OwnCloudClientManagerFactory.getDefaultSingleton().
967 removeClientFor(ocAccount));
968 if (client != null) {
969 OwnCloudCredentials cred = client.getCredentials();
970 if (cred != null) {
971 AccountManager am = AccountManager.get(context);
972 if (cred.authTokenExpires()) {
973 am.invalidateAuthToken(
974 getAccount().type,
975 cred.getAuthToken()
976 );
977 } else {
978 am.clearPassword(getAccount());
979 }
980 }
981 }
982 requestCredentialsUpdate();
983
984 } catch (AccountNotFoundException e) {
985 Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
986 }
987
988 }
989
990 }
991 removeStickyBroadcast(intent);
992 Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
993 mProgressBar.setIndeterminate(mSyncInProgress);
994 //mProgressBar.setVisibility((mSyncInProgress) ? View.VISIBLE : View.INVISIBLE);
995 //setSupportProgressBarIndeterminateVisibility(mSyncInProgress
996 /*|| mRefreshSharesInProgress*/ //);
997
998 setBackgroundText();
999
1000 }
1001
1002 if (synchResult != null) {
1003 if (synchResult.getCode().equals(
1004 RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
1005 mLastSslUntrustedServerResult = synchResult;
1006 }
1007 }
1008 } catch (RuntimeException e) {
1009 // avoid app crashes after changing the serial id of RemoteOperationResult
1010 // in owncloud library with broadcast notifications pending to process
1011 removeStickyBroadcast(intent);
1012 }
1013 }
1014 }
1015
1016 /**
1017 * Show a text message on screen view for notifying user if content is
1018 * loading or folder is empty
1019 */
1020 private void setBackgroundText() {
1021 OCFileListFragment ocFileListFragment = getListOfFilesFragment();
1022 if (ocFileListFragment != null) {
1023 int message = R.string.file_list_loading;
1024 if (!mSyncInProgress) {
1025 // In case file list is empty
1026 message = R.string.file_list_empty;
1027 }
1028 ocFileListFragment.setMessageForEmptyList(getString(message));
1029 } else {
1030 Log_OC.e(TAG, "OCFileListFragment is null");
1031 }
1032 }
1033
1034 /**
1035 * Once the file upload has finished -> update view
1036 */
1037 private class UploadFinishReceiver extends BroadcastReceiver {
1038 /**
1039 * Once the file upload has finished -> update view
1040 *
1041 * @author David A. Velasco
1042 * {@link BroadcastReceiver} to enable upload feedback in UI
1043 */
1044 @Override
1045 public void onReceive(Context context, Intent intent) {
1046 try {
1047 String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
1048 String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
1049 boolean sameAccount = getAccount() != null && accountName.equals(getAccount().name);
1050 OCFile currentDir = getCurrentDir();
1051 boolean isDescendant = (currentDir != null) && (uploadedRemotePath != null) &&
1052 (uploadedRemotePath.startsWith(currentDir.getRemotePath()));
1053
1054 if (sameAccount && isDescendant) {
1055 String linkedToRemotePath =
1056 intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
1057 if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
1058 refreshListOfFilesFragment();
1059 }
1060 }
1061
1062 boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT,
1063 false);
1064 boolean renamedInUpload = getFile().getRemotePath().
1065 equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
1066 boolean sameFile = getFile().getRemotePath().equals(uploadedRemotePath) ||
1067 renamedInUpload;
1068 FileFragment details = getSecondFragment();
1069 boolean detailFragmentIsShown = (details != null &&
1070 details instanceof FileDetailFragment);
1071
1072 if (sameAccount && sameFile && detailFragmentIsShown) {
1073 if (uploadWasFine) {
1074 setFile(getStorageManager().getFileByPath(uploadedRemotePath));
1075 }
1076 if (renamedInUpload) {
1077 String newName = (new File(uploadedRemotePath)).getName();
1078 Toast msg = Toast.makeText(
1079 context,
1080 String.format(
1081 getString(R.string.filedetails_renamed_in_upload_msg),
1082 newName),
1083 Toast.LENGTH_LONG);
1084 msg.show();
1085 }
1086 if (uploadWasFine || getFile().fileExists()) {
1087 ((FileDetailFragment) details).updateFileDetails(false, true);
1088 } else {
1089 cleanSecondFragment();
1090 }
1091
1092 // Force the preview if the file is an image or text file
1093 if (uploadWasFine) {
1094 OCFile ocFile = getFile();
1095 if (PreviewImageFragment.canBePreviewed(ocFile))
1096 startImagePreview(getFile());
1097 else if (PreviewTextFragment.canBePreviewed(ocFile))
1098 startTextPreview(ocFile);
1099 // TODO what about other kind of previews?
1100 }
1101 }
1102
1103 mProgressBar.setIndeterminate(false);
1104 } finally {
1105 if (intent != null) {
1106 removeStickyBroadcast(intent);
1107 }
1108 }
1109
1110 }
1111
1112 // TODO refactor this receiver, and maybe DownloadFinishReceiver; this method is duplicated :S
1113 private boolean isAscendant(String linkedToRemotePath) {
1114 OCFile currentDir = getCurrentDir();
1115 return (
1116 currentDir != null &&
1117 currentDir.getRemotePath().startsWith(linkedToRemotePath)
1118 );
1119 }
1120
1121
1122 }
1123
1124
1125 /**
1126 * Class waiting for broadcast events from the {@link FileDownloader} service.
1127 *
1128 * Updates the UI when a download is started or finished, provided that it is relevant for the
1129 * current folder.
1130 */
1131 private class DownloadFinishReceiver extends BroadcastReceiver {
1132
1133 @Override
1134 public void onReceive(Context context, Intent intent) {
1135 try {
1136 boolean sameAccount = isSameAccount(intent);
1137 String downloadedRemotePath =
1138 intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
1139 boolean isDescendant = isDescendant(downloadedRemotePath);
1140
1141 if (sameAccount && isDescendant) {
1142 String linkedToRemotePath =
1143 intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH);
1144 if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) {
1145 refreshListOfFilesFragment();
1146 }
1147 refreshSecondFragment(
1148 intent.getAction(),
1149 downloadedRemotePath,
1150 intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false)
1151 );
1152 }
1153
1154 if (mWaitingToSend != null) {
1155 mWaitingToSend =
1156 getStorageManager().getFileByPath(mWaitingToSend.getRemotePath());
1157 if (mWaitingToSend.isDown()) {
1158 sendDownloadedFile();
1159 }
1160 }
1161
1162 } finally {
1163 if (intent != null) {
1164 removeStickyBroadcast(intent);
1165 }
1166 }
1167 }
1168
1169 private boolean isDescendant(String downloadedRemotePath) {
1170 OCFile currentDir = getCurrentDir();
1171 return (
1172 currentDir != null &&
1173 downloadedRemotePath != null &&
1174 downloadedRemotePath.startsWith(currentDir.getRemotePath())
1175 );
1176 }
1177
1178 private boolean isAscendant(String linkedToRemotePath) {
1179 OCFile currentDir = getCurrentDir();
1180 return (
1181 currentDir != null &&
1182 currentDir.getRemotePath().startsWith(linkedToRemotePath)
1183 );
1184 }
1185
1186 private boolean isSameAccount(Intent intent) {
1187 String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
1188 return (accountName != null && getAccount() != null &&
1189 accountName.equals(getAccount().name));
1190 }
1191 }
1192
1193
1194 public void browseToRoot() {
1195 OCFileListFragment listOfFiles = getListOfFilesFragment();
1196 if (listOfFiles != null) { // should never be null, indeed
1197 OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
1198 listOfFiles.listDirectory(root);
1199 // TODO Enable when "On Device" is recovered ?
1200 // listOfFiles.listDirectory(root, MainApp.getOnlyOnDevice());
1201 setFile(listOfFiles.getCurrentFile());
1202 startSyncFolderOperation(root, false);
1203 }
1204 cleanSecondFragment();
1205 }
1206
1207
1208 /**
1209 * {@inheritDoc}
1210 * <p/>
1211 * Updates action bar and second fragment, if in dual pane mode.
1212 */
1213 @Override
1214 public void onBrowsedDownTo(OCFile directory) {
1215 setFile(directory);
1216 cleanSecondFragment();
1217 // Sync Folder
1218 startSyncFolderOperation(directory, false);
1219 }
1220
1221 /**
1222 * Shows the information of the {@link OCFile} received as a
1223 * parameter in the second fragment.
1224 *
1225 * @param file {@link OCFile} whose details will be shown
1226 */
1227 @Override
1228 public void showDetails(OCFile file) {
1229 Fragment detailFragment = FileDetailFragment.newInstance(file, getAccount());
1230 setSecondFragment(detailFragment);
1231 updateFragmentsVisibility(true);
1232 updateActionBarTitleAndHomeButton(file);
1233 setFile(file);
1234 }
1235
1236 @Override
1237 protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
1238 if (mDualPane) {
1239 // in dual pane mode, keep the focus of title an action bar in the current folder
1240 super.updateActionBarTitleAndHomeButton(getCurrentDir());
1241
1242 } else {
1243 super.updateActionBarTitleAndHomeButton(chosenFile);
1244 }
1245
1246 }
1247
1248 @Override
1249 protected ServiceConnection newTransferenceServiceConnection() {
1250 return new ListServiceConnection();
1251 }
1252
1253 /**
1254 * Defines callbacks for service binding, passed to bindService()
1255 */
1256 private class ListServiceConnection implements ServiceConnection {
1257
1258 @Override
1259 public void onServiceConnected(ComponentName component, IBinder service) {
1260 if (component.equals(new ComponentName(
1261 FileDisplayActivity.this, FileDownloader.class))) {
1262 Log_OC.d(TAG, "Download service connected");
1263 mDownloaderBinder = (FileDownloaderBinder) service;
1264 if (mWaitingToPreview != null)
1265 if (getStorageManager() != null) {
1266 // update the file
1267 mWaitingToPreview =
1268 getStorageManager().getFileById(mWaitingToPreview.getFileId());
1269 if (!mWaitingToPreview.isDown()) {
1270 requestForDownload();
1271 }
1272 }
1273
1274 } else if (component.equals(new ComponentName(FileDisplayActivity.this,
1275 FileUploader.class))) {
1276 Log_OC.d(TAG, "Upload service connected");
1277 mUploaderBinder = (FileUploaderBinder) service;
1278 } else {
1279 return;
1280 }
1281 // a new chance to get the mDownloadBinder through
1282 // getFileDownloadBinder() - THIS IS A MESS
1283 OCFileListFragment listOfFiles = getListOfFilesFragment();
1284 if (listOfFiles != null) {
1285 listOfFiles.listDirectory();
1286 // TODO Enable when "On Device" is recovered ?
1287 // listOfFiles.listDirectory(MainApp.getOnlyOnDevice());
1288 }
1289 FileFragment secondFragment = getSecondFragment();
1290 if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
1291 FileDetailFragment detailFragment = (FileDetailFragment) secondFragment;
1292 detailFragment.listenForTransferProgress();
1293 detailFragment.updateFileDetails(false, false);
1294 }
1295 }
1296
1297 @Override
1298 public void onServiceDisconnected(ComponentName component) {
1299 if (component.equals(new ComponentName(FileDisplayActivity.this,
1300 FileDownloader.class))) {
1301 Log_OC.d(TAG, "Download service disconnected");
1302 mDownloaderBinder = null;
1303 } else if (component.equals(new ComponentName(FileDisplayActivity.this,
1304 FileUploader.class))) {
1305 Log_OC.d(TAG, "Upload service disconnected");
1306 mUploaderBinder = null;
1307 }
1308 }
1309 }
1310
1311 @Override
1312 public void onSavedCertificate() {
1313 startSyncFolderOperation(getCurrentDir(), false);
1314 }
1315
1316
1317 @Override
1318 public void onFailedSavingCertificate() {
1319 ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(
1320 R.string.ssl_validator_not_saved, new String[]{}, R.string.common_ok, -1, -1
1321 );
1322 dialog.show(getSupportFragmentManager(), DIALOG_CERT_NOT_SAVED);
1323 }
1324
1325 @Override
1326 public void onCancelCertificate() {
1327 // nothing to do
1328 }
1329
1330 /**
1331 * Updates the view associated to the activity after the finish of some operation over files
1332 * in the current account.
1333 *
1334 * @param operation Removal operation performed.
1335 * @param result Result of the removal.
1336 */
1337 @Override
1338 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
1339 super.onRemoteOperationFinish(operation, result);
1340
1341 if (operation instanceof RemoveFileOperation) {
1342 onRemoveFileOperationFinish((RemoveFileOperation) operation, result);
1343
1344 } else if (operation instanceof RenameFileOperation) {
1345 onRenameFileOperationFinish((RenameFileOperation) operation, result);
1346
1347 } else if (operation instanceof SynchronizeFileOperation) {
1348 onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result);
1349
1350 } else if (operation instanceof CreateFolderOperation) {
1351 onCreateFolderOperationFinish((CreateFolderOperation) operation, result);
1352
1353 } else if (operation instanceof MoveFileOperation) {
1354 onMoveFileOperationFinish((MoveFileOperation) operation, result);
1355
1356 } else if (operation instanceof CopyFileOperation) {
1357 onCopyFileOperationFinish((CopyFileOperation) operation, result);
1358 }
1359
1360 }
1361
1362 private void refreshShowDetails() {
1363 FileFragment details = getSecondFragment();
1364 if (details != null) {
1365 OCFile file = details.getFile();
1366 if (file != null) {
1367 file = getStorageManager().getFileByPath(file.getRemotePath());
1368 if (details instanceof PreviewMediaFragment) {
1369 // Refresh OCFile of the fragment
1370 ((PreviewMediaFragment) details).updateFile(file);
1371 } else if (details instanceof PreviewTextFragment) {
1372 // Refresh OCFile of the fragment
1373 ((PreviewTextFragment) details).updateFile(file);
1374 } else {
1375 showDetails(file);
1376 }
1377 }
1378 invalidateOptionsMenu();
1379 }
1380 }
1381
1382 /**
1383 * Updates the view associated to the activity after the finish of an operation trying to
1384 * remove a file.
1385 *
1386 * @param operation Removal operation performed.
1387 * @param result Result of the removal.
1388 */
1389 private void onRemoveFileOperationFinish(RemoveFileOperation operation,
1390 RemoteOperationResult result) {
1391 Toast msg = Toast.makeText(this,
1392 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
1393 Toast.LENGTH_LONG);
1394 msg.show();
1395
1396 if (result.isSuccess()) {
1397 OCFile removedFile = operation.getFile();
1398 FileFragment second = getSecondFragment();
1399 if (second != null && removedFile.equals(second.getFile())) {
1400 if (second instanceof PreviewMediaFragment) {
1401 ((PreviewMediaFragment) second).stopPreview(true);
1402 }
1403 setFile(getStorageManager().getFileById(removedFile.getParentId()));
1404 cleanSecondFragment();
1405 }
1406 if (getStorageManager().getFileById(removedFile.getParentId()).equals(getCurrentDir())){
1407 refreshListOfFilesFragment();
1408 }
1409 invalidateOptionsMenu();
1410 } else {
1411 if (result.isSslRecoverableException()) {
1412 mLastSslUntrustedServerResult = result;
1413 showUntrustedCertDialog(mLastSslUntrustedServerResult);
1414 }
1415 }
1416 }
1417
1418
1419 /**
1420 * Updates the view associated to the activity after the finish of an operation trying to move a
1421 * file.
1422 *
1423 * @param operation Move operation performed.
1424 * @param result Result of the move operation.
1425 */
1426 private void onMoveFileOperationFinish(MoveFileOperation operation,
1427 RemoteOperationResult result) {
1428 if (result.isSuccess()) {
1429 refreshListOfFilesFragment();
1430 } else {
1431 try {
1432 Toast msg = Toast.makeText(FileDisplayActivity.this,
1433 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
1434 Toast.LENGTH_LONG);
1435 msg.show();
1436
1437 } catch (NotFoundException e) {
1438 Log_OC.e(TAG, "Error while trying to show fail message ", e);
1439 }
1440 }
1441 }
1442
1443 /**
1444 * Updates the view associated to the activity after the finish of an operation trying to copy a
1445 * file.
1446 *
1447 * @param operation Copy operation performed.
1448 * @param result Result of the copy operation.
1449 */
1450 private void onCopyFileOperationFinish(CopyFileOperation operation, RemoteOperationResult result) {
1451 if (result.isSuccess()) {
1452 refreshListOfFilesFragment();
1453 } else {
1454 try {
1455 Toast msg = Toast.makeText(FileDisplayActivity.this,
1456 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
1457 Toast.LENGTH_LONG);
1458 msg.show();
1459
1460 } catch (NotFoundException e) {
1461 Log_OC.e(TAG, "Error while trying to show fail message ", e);
1462 }
1463 }
1464 }
1465
1466 /**
1467 * Updates the view associated to the activity after the finish of an operation trying to rename
1468 * a file.
1469 *
1470 * @param operation Renaming operation performed.
1471 * @param result Result of the renaming.
1472 */
1473 private void onRenameFileOperationFinish(RenameFileOperation operation,
1474 RemoteOperationResult result) {
1475 OCFile renamedFile = operation.getFile();
1476 if (result.isSuccess()) {
1477 FileFragment details = getSecondFragment();
1478 if (details != null) {
1479 if (details instanceof FileDetailFragment &&
1480 renamedFile.equals(details.getFile()) ) {
1481 ((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
1482 showDetails(renamedFile);
1483
1484 } else if (details instanceof PreviewMediaFragment &&
1485 renamedFile.equals(details.getFile())) {
1486 ((PreviewMediaFragment) details).updateFile(renamedFile);
1487 if (PreviewMediaFragment.canBePreviewed(renamedFile)) {
1488 int position = ((PreviewMediaFragment) details).getPosition();
1489 startMediaPreview(renamedFile, position, true);
1490 } else {
1491 getFileOperationsHelper().openFile(renamedFile);
1492 }
1493 } else if (details instanceof PreviewTextFragment &&
1494 renamedFile.equals(details.getFile())) {
1495 ((PreviewTextFragment) details).updateFile(renamedFile);
1496 if (PreviewTextFragment.canBePreviewed(renamedFile)) {
1497 startTextPreview(renamedFile);
1498 } else {
1499 getFileOperationsHelper().openFile(renamedFile);
1500 }
1501 }
1502 }
1503
1504 if (getStorageManager().getFileById(renamedFile.getParentId()).equals(getCurrentDir())){
1505 refreshListOfFilesFragment();
1506 }
1507
1508 } else {
1509 Toast msg = Toast.makeText(this,
1510 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
1511 Toast.LENGTH_LONG);
1512 msg.show();
1513
1514 if (result.isSslRecoverableException()) {
1515 mLastSslUntrustedServerResult = result;
1516 showUntrustedCertDialog(mLastSslUntrustedServerResult);
1517 }
1518 }
1519 }
1520
1521 private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation,
1522 RemoteOperationResult result) {
1523 if (result.isSuccess()) {
1524 if (operation.transferWasRequested()) {
1525 OCFile syncedFile = operation.getLocalFile();
1526 onTransferStateChanged(syncedFile, true, true);
1527 invalidateOptionsMenu();
1528 refreshShowDetails();
1529 }
1530 }
1531 }
1532
1533 /**
1534 * Updates the view associated to the activity after the finish of an operation trying create a
1535 * new folder
1536 *
1537 * @param operation Creation operation performed.
1538 * @param result Result of the creation.
1539 */
1540 private void onCreateFolderOperationFinish(CreateFolderOperation operation,
1541 RemoteOperationResult result) {
1542 if (result.isSuccess()) {
1543 refreshListOfFilesFragment();
1544 } else {
1545 try {
1546 Toast msg = Toast.makeText(FileDisplayActivity.this,
1547 ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
1548 Toast.LENGTH_LONG);
1549 msg.show();
1550
1551 } catch (NotFoundException e) {
1552 Log_OC.e(TAG, "Error while trying to show fail message ", e);
1553 }
1554 }
1555 }
1556
1557
1558 /**
1559 * {@inheritDoc}
1560 */
1561 @Override
1562 public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
1563 refreshListOfFilesFragment();
1564 FileFragment details = getSecondFragment();
1565 if (details != null && details instanceof FileDetailFragment &&
1566 file.equals(details.getFile()) ) {
1567 if (downloading || uploading) {
1568 ((FileDetailFragment) details).updateFileDetails(file, getAccount());
1569 } else {
1570 if (!file.fileExists()) {
1571 cleanSecondFragment();
1572 } else {
1573 ((FileDetailFragment) details).updateFileDetails(false, true);
1574 }
1575 }
1576 }
1577
1578 }
1579
1580
1581 private void requestForDownload() {
1582 Account account = getAccount();
1583 //if (!mWaitingToPreview.isDownloading()) {
1584 if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
1585 Intent i = new Intent(this, FileDownloader.class);
1586 i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
1587 i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
1588 startService(i);
1589 }
1590 }
1591
1592
1593 private OCFile getCurrentDir() {
1594 OCFile file = getFile();
1595 if (file != null) {
1596 if (file.isFolder()) {
1597 return file;
1598 } else if (getStorageManager() != null) {
1599 String parentPath = file.getRemotePath().substring(0,
1600 file.getRemotePath().lastIndexOf(file.getFileName()));
1601 return getStorageManager().getFileByPath(parentPath);
1602 }
1603 }
1604 return null;
1605 }
1606
1607 /**
1608 * Starts an operation to refresh the requested folder.
1609 *
1610 * The operation is run in a new background thread created on the fly.
1611 *
1612 * The refresh updates is a "light sync": properties of regular files in folder are updated (including
1613 * associated shares), but not their contents. Only the contents of files marked to be kept-in-sync are
1614 * synchronized too.
1615 *
1616 * @param folder Folder to refresh.
1617 * @param ignoreETag If 'true', the data from the server will be fetched and sync'ed even if the eTag
1618 * didn't change.
1619 */
1620 public void startSyncFolderOperation(final OCFile folder, final boolean ignoreETag) {
1621
1622 // the execution is slightly delayed to allow the activity get the window focus if it's being started
1623 // or if the method is called from a dialog that is being dismissed
1624 getHandler().postDelayed(
1625 new Runnable() {
1626 @Override
1627 public void run() {
1628 if (hasWindowFocus()) {
1629 long currentSyncTime = System.currentTimeMillis();
1630 mSyncInProgress = true;
1631
1632 // perform folder synchronization
1633 RemoteOperation synchFolderOp = new RefreshFolderOperation(folder,
1634 currentSyncTime,
1635 false,
1636 getFileOperationsHelper().isSharedSupported(),
1637 ignoreETag,
1638 getStorageManager(),
1639 getAccount(),
1640 getApplicationContext()
1641 );
1642 synchFolderOp.execute(
1643 getAccount(),
1644 MainApp.getAppContext(),
1645 FileDisplayActivity.this,
1646 null,
1647 null
1648 );
1649
1650 mProgressBar.setIndeterminate(true);
1651
1652 setBackgroundText();
1653
1654 } // else: NOTHING ; lets' not refresh when the user rotates the device but there is
1655 // another window floating over
1656 }
1657 },
1658 DELAY_TO_REQUEST_OPERATIONS_LATER
1659 );
1660
1661 }
1662
1663 /**
1664 * Show untrusted cert dialog
1665 */
1666 public void showUntrustedCertDialog(RemoteOperationResult result) {
1667 // Show a dialog with the certificate info
1668 SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstanceForFullSslError(
1669 (CertificateCombinedException) result.getException());
1670 FragmentManager fm = getSupportFragmentManager();
1671 FragmentTransaction ft = fm.beginTransaction();
1672 dialog.show(ft, DIALOG_UNTRUSTED_CERT);
1673 }
1674
1675 private void requestForDownload(OCFile file) {
1676 Account account = getAccount();
1677 if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
1678 Intent i = new Intent(this, FileDownloader.class);
1679 i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
1680 i.putExtra(FileDownloader.EXTRA_FILE, file);
1681 startService(i);
1682 }
1683 }
1684
1685 private void sendDownloadedFile() {
1686 getFileOperationsHelper().sendDownloadedFile(mWaitingToSend);
1687 mWaitingToSend = null;
1688 }
1689
1690
1691 /**
1692 * Requests the download of the received {@link OCFile} , updates the UI
1693 * to monitor the download progress and prepares the activity to send the file
1694 * when the download finishes.
1695 *
1696 * @param file {@link OCFile} to download and preview.
1697 */
1698 public void startDownloadForSending(OCFile file) {
1699 mWaitingToSend = file;
1700 requestForDownload(mWaitingToSend);
1701 boolean hasSecondFragment = (getSecondFragment() != null);
1702 updateFragmentsVisibility(hasSecondFragment);
1703 }
1704
1705 /**
1706 * Opens the image gallery showing the image {@link OCFile} received as parameter.
1707 *
1708 * @param file Image {@link OCFile} to show.
1709 */
1710 public void startImagePreview(OCFile file) {
1711 Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
1712 showDetailsIntent.putExtra(EXTRA_FILE, file);
1713 showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
1714 startActivity(showDetailsIntent);
1715 }
1716
1717 /**
1718 * Stars the preview of an already down media {@link OCFile}.
1719 *
1720 * @param file Media {@link OCFile} to preview.
1721 * @param startPlaybackPosition Media position where the playback will be started,
1722 * in milliseconds.
1723 * @param autoplay When 'true', the playback will start without user
1724 * interactions.
1725 */
1726 public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
1727 Fragment mediaFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition,
1728 autoplay);
1729 setSecondFragment(mediaFragment);
1730 updateFragmentsVisibility(true);
1731 updateActionBarTitleAndHomeButton(file);
1732 setFile(file);
1733 }
1734
1735 /**
1736 * Stars the preview of a text file {@link OCFile}.
1737 *
1738 * @param file Text {@link OCFile} to preview.
1739 */
1740 public void startTextPreview(OCFile file) {
1741 Bundle args = new Bundle();
1742 args.putParcelable(EXTRA_FILE, file);
1743 args.putParcelable(EXTRA_ACCOUNT, getAccount());
1744 Fragment textPreviewFragment = Fragment.instantiate(getApplicationContext(),
1745 PreviewTextFragment.class.getName(), args);
1746 setSecondFragment(textPreviewFragment);
1747 updateFragmentsVisibility(true);
1748 //updateNavigationElementsInActionBar(file);
1749 setFile(file);
1750 }
1751
1752 /**
1753 * Requests the download of the received {@link OCFile} , updates the UI
1754 * to monitor the download progress and prepares the activity to preview
1755 * or open the file when the download finishes.
1756 *
1757 * @param file {@link OCFile} to download and preview.
1758 */
1759 public void startDownloadForPreview(OCFile file) {
1760 Fragment detailFragment = FileDetailFragment.newInstance(file, getAccount());
1761 setSecondFragment(detailFragment);
1762 mWaitingToPreview = file;
1763 requestForDownload();
1764 updateFragmentsVisibility(true);
1765 updateActionBarTitleAndHomeButton(file);
1766 setFile(file);
1767 }
1768
1769
1770 public void cancelTransference(OCFile file) {
1771 getFileOperationsHelper().cancelTransference(file);
1772 if (mWaitingToPreview != null &&
1773 mWaitingToPreview.getRemotePath().equals(file.getRemotePath())) {
1774 mWaitingToPreview = null;
1775 }
1776 if (mWaitingToSend != null &&
1777 mWaitingToSend.getRemotePath().equals(file.getRemotePath())) {
1778 mWaitingToSend = null;
1779 }
1780 onTransferStateChanged(file, false, false);
1781 }
1782
1783 @Override
1784 public void onRefresh(boolean ignoreETag) {
1785 refreshList(ignoreETag);
1786 }
1787
1788 @Override
1789 public void onRefresh() {
1790 refreshList(true);
1791 }
1792
1793 private void refreshList(boolean ignoreETag) {
1794 OCFileListFragment listOfFiles = getListOfFilesFragment();
1795 if (listOfFiles != null) {
1796 OCFile folder = listOfFiles.getCurrentFile();
1797 if (folder != null) {
1798 /*mFile = mContainerActivity.getStorageManager().getFileById(mFile.getFileId());
1799 listDirectory(mFile);*/
1800 startSyncFolderOperation(folder, ignoreETag);
1801 }
1802 }
1803 }
1804
1805 private void sortByDate(boolean ascending) {
1806 getListOfFilesFragment().sortByDate(ascending);
1807 }
1808
1809 private void sortBySize(boolean ascending) {
1810 getListOfFilesFragment().sortBySize(ascending);
1811 }
1812
1813 private void sortByName(boolean ascending) {
1814 getListOfFilesFragment().sortByName(ascending);
1815 }
1816
1817 public void allFilesOption() {
1818 browseToRoot();
1819 }
1820 }