Copyright note fixes
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / FileDisplayActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 * Copyright (C) 2012-2013 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 package com.owncloud.android.ui.activity;
20
21 import java.io.File;
22
23 import android.accounts.Account;
24 import android.app.AlertDialog;
25 import android.app.ProgressDialog;
26 import android.app.AlertDialog.Builder;
27 import android.app.Dialog;
28 import android.content.BroadcastReceiver;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.DialogInterface.OnClickListener;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.ServiceConnection;
37 import android.content.SharedPreferences;
38 import android.content.SharedPreferences.Editor;
39 import android.content.pm.PackageInfo;
40 import android.content.res.Resources.NotFoundException;
41 import android.database.Cursor;
42 import android.net.Uri;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.preference.PreferenceManager;
47 import android.provider.MediaStore;
48 import android.support.v4.app.Fragment;
49 import android.support.v4.app.FragmentTransaction;
50 import android.util.Log;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.widget.ArrayAdapter;
54 import android.widget.EditText;
55 import android.widget.TextView;
56 import android.widget.Toast;
57
58 import com.actionbarsherlock.app.ActionBar;
59 import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
60 import com.actionbarsherlock.app.SherlockFragmentActivity;
61 import com.actionbarsherlock.view.Menu;
62 import com.actionbarsherlock.view.MenuInflater;
63 import com.actionbarsherlock.view.MenuItem;
64 import com.actionbarsherlock.view.Window;
65 import com.owncloud.android.AccountUtils;
66 import com.owncloud.android.Log_OC;
67 import com.owncloud.android.R;
68 import com.owncloud.android.authenticator.AccountAuthenticator;
69 import com.owncloud.android.datamodel.DataStorageManager;
70 import com.owncloud.android.datamodel.FileDataStorageManager;
71 import com.owncloud.android.datamodel.OCFile;
72 import com.owncloud.android.files.services.FileDownloader;
73 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
74 import com.owncloud.android.files.services.FileObserverService;
75 import com.owncloud.android.files.services.FileUploader;
76 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
77 import com.owncloud.android.network.OwnCloudClientUtils;
78 import com.owncloud.android.operations.OnRemoteOperationListener;
79 import com.owncloud.android.operations.RemoteOperation;
80 import com.owncloud.android.operations.RemoteOperationResult;
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.RemoteOperationResult.ResultCode;
85 import com.owncloud.android.syncadapter.FileSyncService;
86 import com.owncloud.android.ui.dialog.ChangelogDialog;
87 import com.owncloud.android.ui.dialog.EditNameDialog;
88 import com.owncloud.android.ui.dialog.SslValidatorDialog;
89 import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
90 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
91 import com.owncloud.android.ui.fragment.FileDetailFragment;
92 import com.owncloud.android.ui.fragment.FileFragment;
93 import com.owncloud.android.ui.fragment.OCFileListFragment;
94 import com.owncloud.android.ui.preview.PreviewImageActivity;
95 import com.owncloud.android.ui.preview.PreviewImageFragment;
96 import com.owncloud.android.ui.preview.PreviewMediaFragment;
97
98 import eu.alefzero.webdav.WebdavClient;
99
100 /**
101 * Displays, what files the user has available in his ownCloud.
102 *
103 * @author Bartek Przybylski
104 * @author David A. Velasco
105 */
106
107 public class FileDisplayActivity extends SherlockFragmentActivity implements
108 OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
109
110 private ArrayAdapter<String> mDirectories;
111 private OCFile mCurrentDir = null;
112 private OCFile mCurrentFile = null;
113
114 private DataStorageManager mStorageManager;
115 private SyncBroadcastReceiver mSyncBroadcastReceiver;
116 private UploadFinishReceiver mUploadFinishReceiver;
117 private DownloadFinishReceiver mDownloadFinishReceiver;
118 private FileDownloaderBinder mDownloaderBinder = null;
119 private FileUploaderBinder mUploaderBinder = null;
120 private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
121 private RemoteOperationResult mLastSslUntrustedServerResult = null;
122
123 private OCFileListFragment mFileList;
124
125 private boolean mDualPane;
126 private boolean mBackFromCreatingFirstAccount;
127
128 private static final int DIALOG_SETUP_ACCOUNT = 0;
129 private static final int DIALOG_CREATE_DIR = 1;
130 public static final int DIALOG_SHORT_WAIT = 3;
131 private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 4;
132 private static final int DIALOG_SSL_VALIDATOR = 5;
133 private static final int DIALOG_CERT_NOT_SAVED = 6;
134 private static final String DIALOG_CHANGELOG_TAG = "DIALOG_CHANGELOG";
135
136
137 private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
138 private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
139
140 private static final String TAG = "FileDisplayActivity";
141
142 private OCFile mWaitingToPreview;
143 private Handler mHandler;
144
145 @Override
146 public void onCreate(Bundle savedInstanceState) {
147 Log_OC.d(getClass().toString(), "onCreate() start");
148 super.onCreate(savedInstanceState);
149
150 /// Load of parameters from received intent
151 Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
152 if (account != null && AccountUtils.setCurrentOwnCloudAccount(this, account.name)) {
153 mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);
154 }
155
156 /// Load of saved instance state: keep this always before initDataFromCurrentAccount()
157 if(savedInstanceState != null) {
158 // TODO - test if savedInstanceState should take precedence over file in the intent ALWAYS (now), NEVER. SOME TIMES
159 mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
160 mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW);
161
162 } else {
163 mWaitingToPreview = null;
164 }
165
166 if (!AccountUtils.accountsAreSetup(this)) {
167 /// no account available: FORCE ACCOUNT CREATION
168 mStorageManager = null;
169 createFirstAccount();
170
171 } else { /// at least an account is available
172
173 initDataFromCurrentAccount(); // it checks mCurrentDir and mCurrentFile with the current account
174
175 }
176
177 mUploadConnection = new ListServiceConnection();
178 mDownloadConnection = new ListServiceConnection();
179 bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
180 bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
181
182 // PIN CODE request ; best location is to decide, let's try this first
183 if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
184 requestPinCode();
185 }
186
187 // file observer
188 Intent observer_intent = new Intent(this, FileObserverService.class);
189 observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);
190 startService(observer_intent);
191
192
193 /// USER INTERFACE
194 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
195
196 // Drop-down navigation
197 mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
198 OCFile currFile = mCurrentDir;
199 while(mStorageManager != null && currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {
200 mDirectories.add(currFile.getFileName());
201 currFile = mStorageManager.getFileById(currFile.getParentId());
202 }
203 mDirectories.add(OCFile.PATH_SEPARATOR);
204
205 // Inflate and set the layout view
206 setContentView(R.layout.files);
207 mFileList = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
208 mDualPane = (findViewById(R.id.file_details_container) != null);
209 if (mDualPane) {
210 if (savedInstanceState == null) initFileDetailsInDualPane();
211 } else {
212 // quick patchES to fix problem in turn from landscape to portrait, when a file is selected in the right pane
213 // TODO serious refactorization in activities and fragments providing file browsing and handling
214 if (mCurrentFile != null) {
215 onFileClick(mCurrentFile);
216 mCurrentFile = null;
217 }
218 Fragment rightPanel = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
219 if (rightPanel != null) {
220 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
221 transaction.remove(rightPanel);
222 transaction.commit();
223 }
224 }
225
226 // Action bar setup
227 ActionBar actionBar = getSupportActionBar();
228 actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
229 actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getParentId() != 0);
230 actionBar.setDisplayShowTitleEnabled(false);
231 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
232 actionBar.setListNavigationCallbacks(mDirectories, this);
233 setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to workaround bug in its implementation
234
235
236 // show changelog, if needed
237 //showChangeLog();
238 mBackFromCreatingFirstAccount = false;
239
240 Log_OC.d(getClass().toString(), "onCreate() end");
241 }
242
243
244 /**
245 * Shows a dialog with the change log of the current version after each app update
246 *
247 * TODO make it permanent; by now, only to advice the workaround app for 4.1.x
248 */
249 private void showChangeLog() {
250 if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.JELLY_BEAN) {
251 final String KEY_VERSION = "version";
252 SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
253 int currentVersionNumber = 0;
254 int savedVersionNumber = sharedPref.getInt(KEY_VERSION, 0);
255 try {
256 PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
257 currentVersionNumber = pi.versionCode;
258 } catch (Exception e) {}
259
260 if (currentVersionNumber > savedVersionNumber) {
261 ChangelogDialog.newInstance(true).show(getSupportFragmentManager(), DIALOG_CHANGELOG_TAG);
262 Editor editor = sharedPref.edit();
263 editor.putInt(KEY_VERSION, currentVersionNumber);
264 editor.commit();
265 }
266 }
267 }
268
269
270 /**
271 * Launches the account creation activity. To use when no ownCloud account is available
272 */
273 private void createFirstAccount() {
274 Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
275 intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
276 startActivity(intent); // the new activity won't be created until this.onStart() and this.onResume() are finished;
277 }
278
279
280 /**
281 * Load of state dependent of the existence of an ownCloud account
282 */
283 private void initDataFromCurrentAccount() {
284 /// Storage manager initialization - access to local database
285 mStorageManager = new FileDataStorageManager(
286 AccountUtils.getCurrentOwnCloudAccount(this),
287 getContentResolver());
288
289 /// Check if mCurrentDir is a directory
290 if(mCurrentDir != null && !mCurrentDir.isDirectory()) {
291 mCurrentFile = mCurrentDir;
292 mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());
293 }
294
295 /// Check if mCurrentDir and mCurrentFile are in the current account, and update them
296 if (mCurrentDir != null) {
297 mCurrentDir = mStorageManager.getFileByPath(mCurrentDir.getRemotePath()); // mCurrentDir == null if it is not in the current account
298 }
299 if (mCurrentFile != null) {
300 if (mCurrentFile.fileExists()) {
301 mCurrentFile = mStorageManager.getFileByPath(mCurrentFile.getRemotePath()); // mCurrentFile == null if it is not in the current account
302 } // else : keep mCurrentFile with the received value; this is currently the case of an upload in progress, when the user presses the status notification in a landscape tablet
303 }
304
305 /// Default to root if mCurrentDir was not found
306 if (mCurrentDir == null) {
307 mCurrentDir = mStorageManager.getFileByPath("/"); // will be NULL if the database was never synchronized
308 }
309 }
310
311
312 private void initFileDetailsInDualPane() {
313 if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) {
314 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
315 if (mCurrentFile != null) {
316 if (PreviewMediaFragment.canBePreviewed(mCurrentFile)) {
317 if (mCurrentFile.isDown()) {
318 transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
319 } else {
320 transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
321 mWaitingToPreview = mCurrentFile;
322 }
323 } else {
324 transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
325 }
326 mCurrentFile = null;
327
328 } else {
329 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
330 }
331 transaction.commit();
332 }
333 }
334
335
336 @Override
337 public void onDestroy() {
338 super.onDestroy();
339 if (mDownloadConnection != null)
340 unbindService(mDownloadConnection);
341 if (mUploadConnection != null)
342 unbindService(mUploadConnection);
343 }
344
345
346 @Override
347 public boolean onCreateOptionsMenu(Menu menu) {
348 MenuInflater inflater = getSherlock().getMenuInflater();
349 inflater.inflate(R.menu.main_menu, menu);
350
351 return true;
352 }
353
354 @Override
355 public boolean onOptionsItemSelected(MenuItem item) {
356 boolean retval = true;
357 switch (item.getItemId()) {
358 case R.id.action_create_dir: {
359 EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.uploader_info_dirname), "", -1, -1, this);
360 dialog.show(getSupportFragmentManager(), "createdirdialog");
361 break;
362 }
363 case R.id.action_sync_account: {
364 startSynchronization();
365 break;
366 }
367 case R.id.action_upload: {
368 showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
369 break;
370 }
371 case R.id.action_settings: {
372 Intent settingsIntent = new Intent(this, Preferences.class);
373 startActivity(settingsIntent);
374 break;
375 }
376 case android.R.id.home: {
377 if(mCurrentDir != null && mCurrentDir.getParentId() != 0){
378 onBackPressed();
379 }
380 break;
381 }
382 default:
383 retval = super.onOptionsItemSelected(item);
384 }
385 return retval;
386 }
387
388 private void startSynchronization() {
389 ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE); // cancel the current synchronizations of any ownCloud account
390 Bundle bundle = new Bundle();
391 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
392 ContentResolver.requestSync(
393 AccountUtils.getCurrentOwnCloudAccount(this),
394 AccountAuthenticator.AUTH_TOKEN_TYPE, bundle);
395 }
396
397
398 @Override
399 public boolean onNavigationItemSelected(int itemPosition, long itemId) {
400 int i = itemPosition;
401 while (i-- != 0) {
402 onBackPressed();
403 }
404 // the next operation triggers a new call to this method, but it's necessary to
405 // ensure that the name exposed in the action bar is the current directory when the
406 // user selected it in the navigation list
407 if (itemPosition != 0)
408 getSupportActionBar().setSelectedNavigationItem(0);
409 return true;
410 }
411
412 /**
413 * Called, when the user selected something for uploading
414 */
415 public void onActivityResult(int requestCode, int resultCode, Intent data) {
416
417 if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
418 requestSimpleUpload(data, resultCode);
419
420 } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
421 requestMultipleUpload(data, resultCode);
422
423 }
424 }
425
426 private void requestMultipleUpload(Intent data, int resultCode) {
427 String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
428 if (filePaths != null) {
429 String[] remotePaths = new String[filePaths.length];
430 String remotePathBase = "";
431 for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
432 remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
433 }
434 if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))
435 remotePathBase += OCFile.PATH_SEPARATOR;
436 for (int j = 0; j< remotePaths.length; j++) {
437 remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
438 }
439
440 Intent i = new Intent(this, FileUploader.class);
441 i.putExtra(FileUploader.KEY_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
442 i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
443 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
444 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
445 if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
446 i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
447 startService(i);
448
449 } else {
450 Log_OC.d("FileDisplay", "User clicked on 'Update' with no selection");
451 Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);
452 t.show();
453 return;
454 }
455 }
456
457
458 private void requestSimpleUpload(Intent data, int resultCode) {
459 String filepath = null;
460 try {
461 Uri selectedImageUri = data.getData();
462
463 String filemanagerstring = selectedImageUri.getPath();
464 String selectedImagePath = getPath(selectedImageUri);
465
466 if (selectedImagePath != null)
467 filepath = selectedImagePath;
468 else
469 filepath = filemanagerstring;
470
471 } catch (Exception e) {
472 Log_OC.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
473 e.printStackTrace();
474
475 } finally {
476 if (filepath == null) {
477 Log_OC.e("FileDisplay", "Couldnt resolve path to file");
478 Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
479 t.show();
480 return;
481 }
482 }
483
484 Intent i = new Intent(this, FileUploader.class);
485 i.putExtra(FileUploader.KEY_ACCOUNT,
486 AccountUtils.getCurrentOwnCloudAccount(this));
487 String remotepath = new String();
488 for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
489 remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
490 }
491 if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
492 remotepath += OCFile.PATH_SEPARATOR;
493 remotepath += new File(filepath).getName();
494
495 i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
496 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
497 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
498 if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
499 i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
500 startService(i);
501 }
502
503
504 @Override
505 public void onBackPressed() {
506 if (mDirectories.getCount() <= 1) {
507 finish();
508 return;
509 }
510 popDirname();
511 mFileList.onNavigateUp();
512 mCurrentDir = mFileList.getCurrentFile();
513
514 if (mDualPane) {
515 // Resets the FileDetailsFragment on Tablets so that it always displays
516 Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
517 if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
518 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
519 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
520 transaction.commit();
521 }
522 }
523
524 if(mCurrentDir.getParentId() == 0){
525 ActionBar actionBar = getSupportActionBar();
526 actionBar.setDisplayHomeAsUpEnabled(false);
527 }
528 }
529
530 @Override
531 protected void onSaveInstanceState(Bundle outState) {
532 // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
533 Log_OC.d(getClass().toString(), "onSaveInstanceState() start");
534 super.onSaveInstanceState(outState);
535 outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir);
536 if (mDualPane) {
537 FileFragment fragment = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
538 if (fragment != null) {
539 OCFile file = fragment.getFile();
540 if (file != null) {
541 outState.putParcelable(FileDetailFragment.EXTRA_FILE, file);
542 }
543 }
544 }
545 outState.putParcelable(FileDetailActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
546 Log_OC.d(getClass().toString(), "onSaveInstanceState() end");
547 }
548
549
550 @Override
551 protected void onResume() {
552 Log_OC.d(getClass().toString(), "onResume() start");
553 super.onResume();
554
555 if (AccountUtils.accountsAreSetup(this)) {
556
557 if (mStorageManager == null) {
558 // this is necessary for handling the come back to FileDisplayActivity when the first ownCloud account is created
559 initDataFromCurrentAccount();
560 if (mDualPane) {
561 initFileDetailsInDualPane();
562 }
563 mBackFromCreatingFirstAccount = true;
564 }
565
566 // Listen for sync messages
567 IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);
568 mSyncBroadcastReceiver = new SyncBroadcastReceiver();
569 registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
570
571 // Listen for upload messages
572 IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
573 mUploadFinishReceiver = new UploadFinishReceiver();
574 registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
575
576 // Listen for download messages
577 IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_ADDED_MESSAGE);
578 downloadIntentFilter.addAction(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
579 mDownloadFinishReceiver = new DownloadFinishReceiver();
580 registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
581
582 // List current directory
583 mFileList.listDirectory(mCurrentDir); // TODO we should find the way to avoid the need of this (maybe it's not necessary yet; to check)
584
585 } else {
586
587 mStorageManager = null; // an invalid object will be there if all the ownCloud accounts are removed
588 showDialog(DIALOG_SETUP_ACCOUNT);
589
590 }
591 Log_OC.d(getClass().toString(), "onResume() end");
592 }
593
594
595 @Override
596 protected void onPause() {
597 Log_OC.d(getClass().toString(), "onPause() start");
598 super.onPause();
599 if (mSyncBroadcastReceiver != null) {
600 unregisterReceiver(mSyncBroadcastReceiver);
601 mSyncBroadcastReceiver = null;
602 }
603 if (mUploadFinishReceiver != null) {
604 unregisterReceiver(mUploadFinishReceiver);
605 mUploadFinishReceiver = null;
606 }
607 if (mDownloadFinishReceiver != null) {
608 unregisterReceiver(mDownloadFinishReceiver);
609 mDownloadFinishReceiver = null;
610 }
611 if (!AccountUtils.accountsAreSetup(this)) {
612 dismissDialog(DIALOG_SETUP_ACCOUNT);
613 }
614
615 Log_OC.d(getClass().toString(), "onPause() end");
616 }
617
618
619 @Override
620 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
621 if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {
622 ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
623 }
624 }
625
626
627 @Override
628 protected Dialog onCreateDialog(int id) {
629 Dialog dialog = null;
630 AlertDialog.Builder builder;
631 switch (id) {
632 case DIALOG_SETUP_ACCOUNT: {
633 builder = new AlertDialog.Builder(this);
634 builder.setTitle(R.string.main_tit_accsetup);
635 builder.setMessage(R.string.main_wrn_accsetup);
636 builder.setCancelable(false);
637 builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
638 public void onClick(DialogInterface dialog, int which) {
639 createFirstAccount();
640 dialog.dismiss();
641 }
642 });
643 String message = String.format(getString(R.string.common_exit), getString(R.string.app_name));
644 builder.setNegativeButton(message, new OnClickListener() {
645 public void onClick(DialogInterface dialog, int which) {
646 dialog.dismiss();
647 finish();
648 }
649 });
650 //builder.setNegativeButton(android.R.string.cancel, this);
651 dialog = builder.create();
652 break;
653 }
654 case DIALOG_CREATE_DIR: {
655 builder = new Builder(this);
656 final EditText dirNameInput = new EditText(getBaseContext());
657 builder.setView(dirNameInput);
658 builder.setTitle(R.string.uploader_info_dirname);
659 int typed_color = getResources().getColor(R.color.setup_text_typed);
660 dirNameInput.setTextColor(typed_color);
661 builder.setPositiveButton(android.R.string.ok,
662 new OnClickListener() {
663 public void onClick(DialogInterface dialog, int which) {
664 String directoryName = dirNameInput.getText().toString();
665 if (directoryName.trim().length() == 0) {
666 dialog.cancel();
667 return;
668 }
669
670 // Figure out the path where the dir needs to be created
671 String path;
672 if (mCurrentDir == null) {
673 // this is just a patch; we should ensure that mCurrentDir never is null
674 if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
675 OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
676 mStorageManager.saveFile(file);
677 }
678 mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
679 }
680 path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
681
682 // Create directory
683 path += directoryName + OCFile.PATH_SEPARATOR;
684 Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));
685 thread.start();
686
687 dialog.dismiss();
688
689 showDialog(DIALOG_SHORT_WAIT);
690 }
691 });
692 builder.setNegativeButton(R.string.common_cancel,
693 new OnClickListener() {
694 public void onClick(DialogInterface dialog, int which) {
695 dialog.cancel();
696 }
697 });
698 dialog = builder.create();
699 break;
700 }
701 case DIALOG_SHORT_WAIT: {
702 ProgressDialog working_dialog = new ProgressDialog(this);
703 working_dialog.setMessage(getResources().getString(
704 R.string.wait_a_moment));
705 working_dialog.setIndeterminate(true);
706 working_dialog.setCancelable(false);
707 dialog = working_dialog;
708 break;
709 }
710 case DIALOG_CHOOSE_UPLOAD_SOURCE: {
711
712 String[] items = null;
713
714 String[] allTheItems = { getString(R.string.actionbar_upload_files),
715 getString(R.string.actionbar_upload_from_apps),
716 getString(R.string.actionbar_failed_instant_upload) };
717
718 String[] commonItems = { getString(R.string.actionbar_upload_files),
719 getString(R.string.actionbar_upload_from_apps) };
720
721 if (InstantUploadActivity.IS_ENABLED)
722 items = allTheItems;
723 else
724 items = commonItems;
725
726 builder = new AlertDialog.Builder(this);
727 builder.setTitle(R.string.actionbar_upload);
728 builder.setItems(items, new DialogInterface.OnClickListener() {
729 public void onClick(DialogInterface dialog, int item) {
730 if (item == 0) {
731 // if (!mDualPane) {
732 Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
733 action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT,
734 AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this));
735 startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
736 // } else {
737 // TODO create and handle new fragment
738 // LocalFileListFragment
739 // }
740 } else if (item == 1) {
741 Intent action = new Intent(Intent.ACTION_GET_CONTENT);
742 action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
743 startActivityForResult(Intent.createChooser(action, getString(R.string.upload_chooser_title)),
744 ACTION_SELECT_CONTENT_FROM_APPS);
745 } else if (item == 2 && InstantUploadActivity.IS_ENABLED) {
746 Account account = AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this);
747 Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
748 action.putExtra(FileUploader.KEY_ACCOUNT, account);
749 startActivity(action);
750 }
751 }
752 });
753 dialog = builder.create();
754 break;
755 }
756 case DIALOG_SSL_VALIDATOR: {
757 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
758 break;
759 }
760 case DIALOG_CERT_NOT_SAVED: {
761 builder = new AlertDialog.Builder(this);
762 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
763 builder.setCancelable(false);
764 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
765 @Override
766 public void onClick(DialogInterface dialog, int which) {
767 dialog.dismiss();
768 };
769 });
770 dialog = builder.create();
771 break;
772 }
773 default:
774 dialog = null;
775 }
776
777 return dialog;
778 }
779
780
781 /**
782 * Translates a content URI of an image to a physical path
783 * on the disk
784 * @param uri The URI to resolve
785 * @return The path to the image or null if it could not be found
786 */
787 public String getPath(Uri uri) {
788 String[] projection = { MediaStore.Images.Media.DATA };
789 Cursor cursor = managedQuery(uri, projection, null, null, null);
790 if (cursor != null) {
791 int column_index = cursor
792 .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
793 cursor.moveToFirst();
794 return cursor.getString(column_index);
795 }
796 return null;
797 }
798
799 /**
800 * Pushes a directory to the drop down list
801 * @param directory to push
802 * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
803 */
804 public void pushDirname(OCFile directory) {
805 if(!directory.isDirectory()){
806 throw new IllegalArgumentException("Only directories may be pushed!");
807 }
808 mDirectories.insert(directory.getFileName(), 0);
809 mCurrentDir = directory;
810 }
811
812 /**
813 * Pops a directory name from the drop down list
814 * @return True, unless the stack is empty
815 */
816 public boolean popDirname() {
817 mDirectories.remove(mDirectories.getItem(0));
818 return !mDirectories.isEmpty();
819 }
820
821 private class DirectoryCreator implements Runnable {
822 private String mTargetPath;
823 private Account mAccount;
824 private Handler mHandler;
825
826 public DirectoryCreator(String targetPath, Account account, Handler handler) {
827 mTargetPath = targetPath;
828 mAccount = account;
829 mHandler = handler;
830 }
831
832 @Override
833 public void run() {
834 WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
835 boolean created = wdc.createDirectory(mTargetPath);
836 if (created) {
837 mHandler.post(new Runnable() {
838 @Override
839 public void run() {
840 dismissDialog(DIALOG_SHORT_WAIT);
841
842 // Save new directory in local database
843 OCFile newDir = new OCFile(mTargetPath);
844 newDir.setMimetype("DIR");
845 newDir.setParentId(mCurrentDir.getFileId());
846 mStorageManager.saveFile(newDir);
847
848 // Display the new folder right away
849 mFileList.listDirectory();
850 }
851 });
852
853 } else {
854 mHandler.post(new Runnable() {
855 @Override
856 public void run() {
857 dismissDialog(DIALOG_SHORT_WAIT);
858 try {
859 Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
860 msg.show();
861
862 } catch (NotFoundException e) {
863 Log_OC.e(TAG, "Error while trying to show fail message ", e);
864 }
865 }
866 });
867 }
868 }
869
870 }
871
872 // Custom array adapter to override text colors
873 private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
874
875 public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
876 super(ctx, view);
877 }
878
879 public View getView(int position, View convertView, ViewGroup parent) {
880 View v = super.getView(position, convertView, parent);
881
882 ((TextView) v).setTextColor(getResources().getColorStateList(
883 android.R.color.white));
884 return v;
885 }
886
887 public View getDropDownView(int position, View convertView,
888 ViewGroup parent) {
889 View v = super.getDropDownView(position, convertView, parent);
890
891 ((TextView) v).setTextColor(getResources().getColorStateList(
892 android.R.color.white));
893
894 return v;
895 }
896
897 }
898
899 private class SyncBroadcastReceiver extends BroadcastReceiver {
900
901 /**
902 * {@link BroadcastReceiver} to enable syncing feedback in UI
903 */
904 @Override
905 public void onReceive(Context context, Intent intent) {
906 boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
907 String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
908
909 Log_OC.d("FileDisplay", "sync of account " + accountName + " is in_progress: " + inProgress);
910
911 if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {
912
913 String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
914
915 boolean fillBlankRoot = false;
916 if (mCurrentDir == null) {
917 mCurrentDir = mStorageManager.getFileByPath("/");
918 fillBlankRoot = (mCurrentDir != null);
919 }
920
921 if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))
922 || fillBlankRoot ) {
923 if (!fillBlankRoot)
924 mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);
925 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager()
926 .findFragmentById(R.id.fileList);
927 if (fileListFragment != null) {
928 fileListFragment.listDirectory(mCurrentDir);
929 }
930 }
931
932 setSupportProgressBarIndeterminateVisibility(inProgress);
933 if (mBackFromCreatingFirstAccount) {
934 // awful patch to fix problem with visibility of progress circle with the first refresh of the first account
935 // TODO - kill this Activity when the first account has to be created instead of stack the account creation on it
936 getSupportActionBar().hide();
937 getSupportActionBar().show();
938 mBackFromCreatingFirstAccount = false;
939 }
940 removeStickyBroadcast(intent);
941
942 }
943
944 RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
945 if (synchResult != null) {
946 if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
947 mLastSslUntrustedServerResult = synchResult;
948 showDialog(DIALOG_SSL_VALIDATOR);
949 }
950 }
951 }
952 }
953
954
955 private class UploadFinishReceiver extends BroadcastReceiver {
956 /**
957 * Once the file upload has finished -> update view
958 * @author David A. Velasco
959 * {@link BroadcastReceiver} to enable upload feedback in UI
960 */
961 @Override
962 public void onReceive(Context context, Intent intent) {
963 String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
964 String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
965 boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);
966 boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
967 if (sameAccount && isDescendant) {
968 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
969 if (fileListFragment != null) {
970 fileListFragment.listDirectory();
971 }
972 }
973 }
974
975 }
976
977
978 /**
979 * Class waiting for broadcast events from the {@link FielDownloader} service.
980 *
981 * Updates the UI when a download is started or finished, provided that it is relevant for the
982 * current folder.
983 */
984 private class DownloadFinishReceiver extends BroadcastReceiver {
985 @Override
986 public void onReceive(Context context, Intent intent) {
987 boolean sameAccount = isSameAccount(context, intent);
988 String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
989 boolean isDescendant = isDescendant(downloadedRemotePath);
990
991 if (sameAccount && isDescendant) {
992 updateLeftPanel();
993 if (mDualPane) {
994 updateRightPanel(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
995 }
996 }
997
998 removeStickyBroadcast(intent);
999 }
1000
1001 private boolean isDescendant(String downloadedRemotePath) {
1002 return (mCurrentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
1003 }
1004
1005 private boolean isSameAccount(Context context, Intent intent) {
1006 String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
1007 return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name));
1008 }
1009 }
1010
1011
1012 protected void updateLeftPanel() {
1013 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1014 if (fileListFragment != null) {
1015 fileListFragment.listDirectory();
1016 }
1017 }
1018
1019 protected void updateRightPanel(String downloadEvent, String downloadedRemotePath, boolean success) {
1020 Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1021 boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
1022 if (fragment != null && fragment instanceof FileDetailFragment) {
1023 FileDetailFragment detailsFragment = (FileDetailFragment) fragment;
1024 OCFile fileInFragment = detailsFragment.getFile();
1025 if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
1026 // the user browsed to other file ; forget the automatic preview
1027 mWaitingToPreview = null;
1028
1029 } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) {
1030 // grant that the right panel updates the progress bar
1031 detailsFragment.listenForTransferProgress();
1032 detailsFragment.updateFileDetails(true, false);
1033
1034 } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) {
1035 // update the right panel
1036 if (success && waitedPreview) {
1037 mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path
1038 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1039 transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mWaitingToPreview, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1040 transaction.commit();
1041 mWaitingToPreview = null;
1042
1043 } else {
1044 detailsFragment.updateFileDetails(false, (success));
1045 }
1046 }
1047 }
1048 }
1049
1050
1051 /**
1052 * {@inheritDoc}
1053 */
1054 @Override
1055 public DataStorageManager getStorageManager() {
1056 return mStorageManager;
1057 }
1058
1059
1060 /**
1061 * {@inheritDoc}
1062 */
1063 @Override
1064 public void onDirectoryClick(OCFile directory) {
1065 pushDirname(directory);
1066 ActionBar actionBar = getSupportActionBar();
1067 actionBar.setDisplayHomeAsUpEnabled(true);
1068
1069 if (mDualPane) {
1070 // Resets the FileDetailsFragment on Tablets so that it always displays
1071 Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1072 if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
1073 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1074 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
1075 transaction.commit();
1076 }
1077 }
1078 }
1079
1080
1081 /**
1082 * {@inheritDoc}
1083 */
1084 @Override
1085 public void onFileClick(OCFile file) {
1086 if (file != null && PreviewImageFragment.canBePreviewed(file)) {
1087 // preview image - it handles the download, if needed
1088 startPreviewImage(file);
1089
1090 } else if (file != null && PreviewMediaFragment.canBePreviewed(file)) {
1091 if (file.isDown()) {
1092 // general preview
1093 startMediaPreview(file);
1094
1095 } else {
1096 // automatic download, preview on finish
1097 startDownloadForPreview(file);
1098
1099 }
1100 } else {
1101 // details view
1102 startDetails(file);
1103 }
1104 }
1105
1106 private void startPreviewImage(OCFile file) {
1107 Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
1108 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1109 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1110 startActivity(showDetailsIntent);
1111 }
1112
1113 private void startMediaPreview(OCFile file) {
1114 if (mDualPane) {
1115 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1116 transaction.replace(R.id.file_details_container, new PreviewMediaFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1117 transaction.commit();
1118
1119 } else {
1120 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1121 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1122 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1123 startActivity(showDetailsIntent);
1124 }
1125 }
1126
1127 private void startDownloadForPreview(OCFile file) {
1128 if (mDualPane) {
1129 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1130 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1131 transaction.commit();
1132 mWaitingToPreview = file;
1133 requestForDownload();
1134
1135 } else {
1136 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1137 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1138 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1139 startActivity(showDetailsIntent);
1140 }
1141 }
1142
1143
1144 private void startDetails(OCFile file) {
1145 if (mDualPane && !file.isImage()) {
1146 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1147 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1148 transaction.commit();
1149 } else {
1150 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1151 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1152 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1153 startActivity(showDetailsIntent);
1154 }
1155 }
1156
1157
1158 /**
1159 * {@inheritDoc}
1160 */
1161 @Override
1162 public OCFile getInitialDirectory() {
1163 return mCurrentDir;
1164 }
1165
1166
1167 /**
1168 * {@inheritDoc}
1169 */
1170 @Override
1171 public void onFileStateChanged() {
1172 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1173 if (fileListFragment != null) {
1174 fileListFragment.listDirectory();
1175 }
1176 }
1177
1178
1179 /**
1180 * {@inheritDoc}
1181 */
1182 @Override
1183 public FileDownloaderBinder getFileDownloaderBinder() {
1184 return mDownloaderBinder;
1185 }
1186
1187
1188 /**
1189 * {@inheritDoc}
1190 */
1191 @Override
1192 public FileUploaderBinder getFileUploaderBinder() {
1193 return mUploaderBinder;
1194 }
1195
1196
1197 /** Defines callbacks for service binding, passed to bindService() */
1198 private class ListServiceConnection implements ServiceConnection {
1199
1200 @Override
1201 public void onServiceConnected(ComponentName component, IBinder service) {
1202 if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
1203 Log_OC.d(TAG, "Download service connected");
1204 mDownloaderBinder = (FileDownloaderBinder) service;
1205 if (mWaitingToPreview != null) {
1206 requestForDownload();
1207 }
1208
1209 } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
1210 Log_OC.d(TAG, "Upload service connected");
1211 mUploaderBinder = (FileUploaderBinder) service;
1212 } else {
1213 return;
1214 }
1215 // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
1216 if (mFileList != null)
1217 mFileList.listDirectory();
1218 if (mDualPane) {
1219 Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1220 if (fragment != null && fragment instanceof FileDetailFragment) {
1221 FileDetailFragment detailFragment = (FileDetailFragment)fragment;
1222 detailFragment.listenForTransferProgress();
1223 detailFragment.updateFileDetails(false, false);
1224 }
1225 }
1226 }
1227
1228 @Override
1229 public void onServiceDisconnected(ComponentName component) {
1230 if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
1231 Log_OC.d(TAG, "Download service disconnected");
1232 mDownloaderBinder = null;
1233 } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
1234 Log_OC.d(TAG, "Upload service disconnected");
1235 mUploaderBinder = null;
1236 }
1237 }
1238 };
1239
1240
1241
1242 /**
1243 * Launch an intent to request the PIN code to the user before letting him use the app
1244 */
1245 private void requestPinCode() {
1246 boolean pinStart = false;
1247 SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
1248 pinStart = appPrefs.getBoolean("set_pincode", false);
1249 if (pinStart) {
1250 Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
1251 i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
1252 startActivity(i);
1253 }
1254 }
1255
1256
1257 @Override
1258 public void onSavedCertificate() {
1259 startSynchronization();
1260 }
1261
1262
1263 @Override
1264 public void onFailedSavingCertificate() {
1265 showDialog(DIALOG_CERT_NOT_SAVED);
1266 }
1267
1268
1269 /**
1270 * Updates the view associated to the activity after the finish of some operation over files
1271 * in the current account.
1272 *
1273 * @param operation Removal operation performed.
1274 * @param result Result of the removal.
1275 */
1276 @Override
1277 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
1278 if (operation instanceof RemoveFileOperation) {
1279 onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
1280
1281 } else if (operation instanceof RenameFileOperation) {
1282 onRenameFileOperationFinish((RenameFileOperation)operation, result);
1283
1284 } else if (operation instanceof SynchronizeFileOperation) {
1285 onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
1286 }
1287 }
1288
1289
1290 /**
1291 * Updates the view associated to the activity after the finish of an operation trying to remove a
1292 * file.
1293 *
1294 * @param operation Removal operation performed.
1295 * @param result Result of the removal.
1296 */
1297 private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
1298 dismissDialog(DIALOG_SHORT_WAIT);
1299 if (result.isSuccess()) {
1300 Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
1301 msg.show();
1302 OCFile removedFile = operation.getFile();
1303 if (mDualPane) {
1304 FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1305 if (details != null && removedFile.equals(details.getFile())) {
1306 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1307 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
1308 transaction.commit();
1309 }
1310 }
1311 if (mStorageManager.getFileById(removedFile.getParentId()).equals(mCurrentDir)) {
1312 mFileList.listDirectory();
1313 }
1314
1315 } else {
1316 Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG);
1317 msg.show();
1318 if (result.isSslRecoverableException()) {
1319 mLastSslUntrustedServerResult = result;
1320 showDialog(DIALOG_SSL_VALIDATOR);
1321 }
1322 }
1323 }
1324
1325 /**
1326 * Updates the view associated to the activity after the finish of an operation trying to rename a
1327 * file.
1328 *
1329 * @param operation Renaming operation performed.
1330 * @param result Result of the renaming.
1331 */
1332 private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
1333 dismissDialog(DIALOG_SHORT_WAIT);
1334 OCFile renamedFile = operation.getFile();
1335 if (result.isSuccess()) {
1336 if (mDualPane) {
1337 FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1338 if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
1339 ((FileDetailFragment) details).updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this));
1340 }
1341 }
1342 if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) {
1343 mFileList.listDirectory();
1344 }
1345
1346 } else {
1347 if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
1348 Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
1349 msg.show();
1350 // TODO throw again the new rename dialog
1351 } else {
1352 Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
1353 msg.show();
1354 if (result.isSslRecoverableException()) {
1355 mLastSslUntrustedServerResult = result;
1356 showDialog(DIALOG_SSL_VALIDATOR);
1357 }
1358 }
1359 }
1360 }
1361
1362
1363 private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
1364 dismissDialog(DIALOG_SHORT_WAIT);
1365 OCFile syncedFile = operation.getLocalFile();
1366 if (!result.isSuccess()) {
1367 if (result.getCode() == ResultCode.SYNC_CONFLICT) {
1368 Intent i = new Intent(this, ConflictsResolveActivity.class);
1369 i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
1370 i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1371 startActivity(i);
1372
1373 } else {
1374 Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
1375 msg.show();
1376 }
1377
1378 } else {
1379 if (operation.transferWasRequested()) {
1380 mFileList.listDirectory();
1381 onTransferStateChanged(syncedFile, true, true);
1382
1383 } else {
1384 Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
1385 msg.show();
1386 }
1387 }
1388 }
1389
1390
1391 /**
1392 * {@inheritDoc}
1393 */
1394 @Override
1395 public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
1396 /*OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1397 if (fileListFragment != null) {
1398 fileListFragment.listDirectory();
1399 }*/
1400 if (mDualPane) {
1401 FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1402 if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
1403 if (downloading || uploading) {
1404 ((FileDetailFragment)details).updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this));
1405 } else {
1406 ((FileDetailFragment)details).updateFileDetails(false, true);
1407 }
1408 }
1409 }
1410 }
1411
1412
1413 @Override
1414 public void showFragmentWithDetails(OCFile file) {
1415 if (mDualPane) {
1416 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1417 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1418 transaction.commit();
1419
1420 } else {
1421 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1422 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1423 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1424 showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
1425 startActivity(showDetailsIntent);
1426 }
1427 }
1428
1429 public void onDismiss(EditNameDialog dialog) {
1430 //dialog.dismiss();
1431 if (dialog.getResult()) {
1432 String newDirectoryName = dialog.getNewFilename().trim();
1433 Log.d(TAG, "'create directory' dialog dismissed with new name " + newDirectoryName);
1434 if (newDirectoryName.length() > 0) {
1435 String path;
1436 if (mCurrentDir == null) {
1437 // this is just a patch; we should ensure that mCurrentDir never is null
1438 if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
1439 OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
1440 mStorageManager.saveFile(file);
1441 }
1442 mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
1443 }
1444 path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
1445
1446 // Create directory
1447 path += newDirectoryName + OCFile.PATH_SEPARATOR;
1448 Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));
1449 thread.start();
1450
1451 showDialog(DIALOG_SHORT_WAIT);
1452 }
1453 }
1454 }
1455
1456 private void requestForDownload() {
1457 Account account = AccountUtils.getCurrentOwnCloudAccount(this);
1458 if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
1459 Intent i = new Intent(this, FileDownloader.class);
1460 i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
1461 i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
1462 startService(i);
1463 }
1464 }
1465
1466
1467 }