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