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