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