Recovery of instant uploads in temporary disabled
[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
757 String[] items = null;
758
759 String[] allTheItems = { getString(R.string.actionbar_upload_files),
760 getString(R.string.actionbar_upload_from_apps),
761 getString(R.string.actionbar_failed_instant_upload) };
762
763 String[] commonItems = { getString(R.string.actionbar_upload_files),
764 getString(R.string.actionbar_upload_from_apps) };
765
766 if (InstantUploadActivity.IS_ENABLED)
767 items = allTheItems;
768 else
769 items = commonItems;
770
771 builder = new AlertDialog.Builder(this);
772 builder.setTitle(R.string.actionbar_upload);
773 builder.setItems(items, new DialogInterface.OnClickListener() {
774 public void onClick(DialogInterface dialog, int item) {
775 if (item == 0) {
776 // if (!mDualPane) {
777 Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
778 action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT,
779 AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this));
780 startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
781 // } else {
782 // TODO create and handle new fragment
783 // LocalFileListFragment
784 // }
785 } else if (item == 1) {
786 Intent action = new Intent(Intent.ACTION_GET_CONTENT);
787 action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
788 startActivityForResult(Intent.createChooser(action, getString(R.string.upload_chooser_title)),
789 ACTION_SELECT_CONTENT_FROM_APPS);
790 } else if (item == 2 && InstantUploadActivity.IS_ENABLED) {
791 Account account = AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this);
792 Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
793 action.putExtra(FileUploader.KEY_ACCOUNT, account);
794 startActivity(action);
795 }
796 }
797 });
798 dialog = builder.create();
799 break;
800 }
801 case DIALOG_SSL_VALIDATOR: {
802 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
803 break;
804 }
805 case DIALOG_CERT_NOT_SAVED: {
806 builder = new AlertDialog.Builder(this);
807 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
808 builder.setCancelable(false);
809 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
810 @Override
811 public void onClick(DialogInterface dialog, int which) {
812 dialog.dismiss();
813 };
814 });
815 dialog = builder.create();
816 break;
817 }
818 default:
819 dialog = null;
820 }
821
822 return dialog;
823 }
824
825
826 /**
827 * Translates a content URI of an image to a physical path
828 * on the disk
829 * @param uri The URI to resolve
830 * @return The path to the image or null if it could not be found
831 */
832 public String getPath(Uri uri) {
833 String[] projection = { MediaStore.Images.Media.DATA };
834 Cursor cursor = managedQuery(uri, projection, null, null, null);
835 if (cursor != null) {
836 int column_index = cursor
837 .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
838 cursor.moveToFirst();
839 return cursor.getString(column_index);
840 }
841 return null;
842 }
843
844 /**
845 * Pushes a directory to the drop down list
846 * @param directory to push
847 * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
848 */
849 public void pushDirname(OCFile directory) {
850 if(!directory.isDirectory()){
851 throw new IllegalArgumentException("Only directories may be pushed!");
852 }
853 mDirectories.insert(directory.getFileName(), 0);
854 mCurrentDir = directory;
855 }
856
857 /**
858 * Pops a directory name from the drop down list
859 * @return True, unless the stack is empty
860 */
861 public boolean popDirname() {
862 mDirectories.remove(mDirectories.getItem(0));
863 return !mDirectories.isEmpty();
864 }
865
866 private class DirectoryCreator implements Runnable {
867 private String mTargetPath;
868 private Account mAccount;
869 private Handler mHandler;
870
871 public DirectoryCreator(String targetPath, Account account, Handler handler) {
872 mTargetPath = targetPath;
873 mAccount = account;
874 mHandler = handler;
875 }
876
877 @Override
878 public void run() {
879 WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
880 boolean created = wdc.createDirectory(mTargetPath);
881 if (created) {
882 mHandler.post(new Runnable() {
883 @Override
884 public void run() {
885 dismissDialog(DIALOG_SHORT_WAIT);
886
887 // Save new directory in local database
888 OCFile newDir = new OCFile(mTargetPath);
889 newDir.setMimetype("DIR");
890 newDir.setParentId(mCurrentDir.getFileId());
891 mStorageManager.saveFile(newDir);
892
893 // Display the new folder right away
894 mFileList.listDirectory();
895 }
896 });
897
898 } else {
899 mHandler.post(new Runnable() {
900 @Override
901 public void run() {
902 dismissDialog(DIALOG_SHORT_WAIT);
903 try {
904 Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
905 msg.show();
906
907 } catch (NotFoundException e) {
908 Log.e(TAG, "Error while trying to show fail message " , e);
909 }
910 }
911 });
912 }
913 }
914
915 }
916
917 // Custom array adapter to override text colors
918 private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
919
920 public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
921 super(ctx, view);
922 }
923
924 public View getView(int position, View convertView, ViewGroup parent) {
925 View v = super.getView(position, convertView, parent);
926
927 ((TextView) v).setTextColor(getResources().getColorStateList(
928 android.R.color.white));
929 return v;
930 }
931
932 public View getDropDownView(int position, View convertView,
933 ViewGroup parent) {
934 View v = super.getDropDownView(position, convertView, parent);
935
936 ((TextView) v).setTextColor(getResources().getColorStateList(
937 android.R.color.white));
938
939 return v;
940 }
941
942 }
943
944 private class SyncBroadcastReceiver extends BroadcastReceiver {
945
946 /**
947 * {@link BroadcastReceiver} to enable syncing feedback in UI
948 */
949 @Override
950 public void onReceive(Context context, Intent intent) {
951 boolean inProgress = intent.getBooleanExtra(
952 FileSyncService.IN_PROGRESS, false);
953 String accountName = intent
954 .getStringExtra(FileSyncService.ACCOUNT_NAME);
955
956 Log.d("FileDisplay", "sync of account " + accountName
957 + " is in_progress: " + inProgress);
958
959 if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {
960
961 String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
962
963 boolean fillBlankRoot = false;
964 if (mCurrentDir == null) {
965 mCurrentDir = mStorageManager.getFileByPath("/");
966 fillBlankRoot = (mCurrentDir != null);
967 }
968
969 if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))
970 || fillBlankRoot ) {
971 if (!fillBlankRoot)
972 mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);
973 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager()
974 .findFragmentById(R.id.fileList);
975 if (fileListFragment != null) {
976 fileListFragment.listDirectory(mCurrentDir);
977 }
978 }
979
980 setSupportProgressBarIndeterminateVisibility(inProgress);
981 removeStickyBroadcast(intent);
982
983 }
984
985 RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
986 if (synchResult != null) {
987 if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
988 mLastSslUntrustedServerResult = synchResult;
989 showDialog(DIALOG_SSL_VALIDATOR);
990 }
991 }
992 }
993 }
994
995
996 private class UploadFinishReceiver extends BroadcastReceiver {
997 /**
998 * Once the file upload has finished -> update view
999 * @author David A. Velasco
1000 * {@link BroadcastReceiver} to enable upload feedback in UI
1001 */
1002 @Override
1003 public void onReceive(Context context, Intent intent) {
1004 String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
1005 String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
1006 boolean sameAccount = accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name);
1007 boolean isDescendant = (mCurrentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
1008 if (sameAccount && isDescendant) {
1009 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1010 if (fileListFragment != null) {
1011 fileListFragment.listDirectory();
1012 }
1013 }
1014 }
1015
1016 }
1017
1018
1019 /**
1020 * Class waiting for broadcast events from the {@link FielDownloader} service.
1021 *
1022 * Updates the UI when a download is started or finished, provided that it is relevant for the
1023 * current folder.
1024 */
1025 private class DownloadFinishReceiver extends BroadcastReceiver {
1026 @Override
1027 public void onReceive(Context context, Intent intent) {
1028 boolean sameAccount = isSameAccount(context, intent);
1029 String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
1030 boolean isDescendant = isDescendant(downloadedRemotePath);
1031
1032 if (sameAccount && isDescendant) {
1033 updateLeftPanel();
1034 if (mDualPane) {
1035 updateRightPanel(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
1036 }
1037 }
1038
1039 removeStickyBroadcast(intent);
1040 }
1041
1042 private boolean isDescendant(String downloadedRemotePath) {
1043 return (mCurrentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(mCurrentDir.getRemotePath()));
1044 }
1045
1046 private boolean isSameAccount(Context context, Intent intent) {
1047 String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
1048 return (accountName != null && accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name));
1049 }
1050 }
1051
1052
1053 protected void updateLeftPanel() {
1054 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1055 if (fileListFragment != null) {
1056 fileListFragment.listDirectory();
1057 }
1058 }
1059
1060 protected void updateRightPanel(String downloadEvent, String downloadedRemotePath, boolean success) {
1061 Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1062 boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
1063 if (fragment != null && fragment instanceof FileDetailFragment) {
1064 FileDetailFragment detailsFragment = (FileDetailFragment) fragment;
1065 OCFile fileInFragment = detailsFragment.getFile();
1066 if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
1067 // the user browsed to other file ; forget the automatic preview
1068 mWaitingToPreview = null;
1069
1070 } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_ADDED_MESSAGE)) {
1071 // grant that the right panel updates the progress bar
1072 detailsFragment.listenForTransferProgress();
1073 detailsFragment.updateFileDetails(true, false);
1074
1075 } else if (downloadEvent.equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) {
1076 // update the right panel
1077 if (success && waitedPreview) {
1078 mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path
1079 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1080 transaction.replace(R.id.file_details_container, new PreviewMediaFragment(mWaitingToPreview, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1081 transaction.commit();
1082 mWaitingToPreview = null;
1083
1084 } else {
1085 detailsFragment.updateFileDetails(false, (success));
1086 }
1087 }
1088 }
1089 }
1090
1091
1092 /**
1093 * {@inheritDoc}
1094 */
1095 @Override
1096 public DataStorageManager getStorageManager() {
1097 return mStorageManager;
1098 }
1099
1100
1101 /**
1102 * {@inheritDoc}
1103 */
1104 @Override
1105 public void onDirectoryClick(OCFile directory) {
1106 pushDirname(directory);
1107 ActionBar actionBar = getSupportActionBar();
1108 actionBar.setDisplayHomeAsUpEnabled(true);
1109
1110 if (mDualPane) {
1111 // Resets the FileDetailsFragment on Tablets so that it always displays
1112 Fragment fileFragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1113 if (fileFragment != null && (fileFragment instanceof PreviewMediaFragment || !((FileDetailFragment) fileFragment).isEmpty())) {
1114 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1115 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
1116 transaction.commit();
1117 }
1118 }
1119 }
1120
1121
1122 /**
1123 * {@inheritDoc}
1124 */
1125 @Override
1126 public void onFileClick(OCFile file) {
1127 if (file != null && PreviewImageFragment.canBePreviewed(file)) {
1128 // preview image - it handles the download, if needed
1129 startPreviewImage(file);
1130
1131 } else if (file != null && PreviewMediaFragment.canBePreviewed(file)) {
1132 if (file.isDown()) {
1133 // general preview
1134 startMediaPreview(file);
1135
1136 } else {
1137 // automatic download, preview on finish
1138 startDownloadForPreview(file);
1139
1140 }
1141 } else {
1142 // details view
1143 startDetails(file);
1144 }
1145 }
1146
1147 private void startPreviewImage(OCFile file) {
1148 Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
1149 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1150 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1151 startActivity(showDetailsIntent);
1152 }
1153
1154 private void startMediaPreview(OCFile file) {
1155 if (mDualPane) {
1156 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1157 transaction.replace(R.id.file_details_container, new PreviewMediaFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1158 transaction.commit();
1159
1160 } else {
1161 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1162 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1163 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1164 startActivity(showDetailsIntent);
1165 }
1166 }
1167
1168 private void startDownloadForPreview(OCFile file) {
1169 if (mDualPane) {
1170 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1171 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1172 transaction.commit();
1173 mWaitingToPreview = file;
1174 requestForDownload();
1175
1176 } else {
1177 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1178 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1179 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1180 startActivity(showDetailsIntent);
1181 }
1182 }
1183
1184
1185 private void startDetails(OCFile file) {
1186 if (mDualPane && !file.isImage()) {
1187 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1188 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1189 transaction.commit();
1190 } else {
1191 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1192 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1193 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1194 startActivity(showDetailsIntent);
1195 }
1196 }
1197
1198
1199 /**
1200 * {@inheritDoc}
1201 */
1202 @Override
1203 public OCFile getInitialDirectory() {
1204 return mCurrentDir;
1205 }
1206
1207
1208 /**
1209 * {@inheritDoc}
1210 */
1211 @Override
1212 public void onFileStateChanged() {
1213 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1214 if (fileListFragment != null) {
1215 fileListFragment.listDirectory();
1216 }
1217 }
1218
1219
1220 /**
1221 * {@inheritDoc}
1222 */
1223 @Override
1224 public FileDownloaderBinder getFileDownloaderBinder() {
1225 return mDownloaderBinder;
1226 }
1227
1228
1229 /**
1230 * {@inheritDoc}
1231 */
1232 @Override
1233 public FileUploaderBinder getFileUploaderBinder() {
1234 return mUploaderBinder;
1235 }
1236
1237
1238 /** Defines callbacks for service binding, passed to bindService() */
1239 private class ListServiceConnection implements ServiceConnection {
1240
1241 @Override
1242 public void onServiceConnected(ComponentName component, IBinder service) {
1243 if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
1244 Log.d(TAG, "Download service connected");
1245 mDownloaderBinder = (FileDownloaderBinder) service;
1246 if (mWaitingToPreview != null) {
1247 requestForDownload();
1248 }
1249
1250 } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
1251 Log.d(TAG, "Upload service connected");
1252 mUploaderBinder = (FileUploaderBinder) service;
1253 } else {
1254 return;
1255 }
1256 // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
1257 if (mFileList != null)
1258 mFileList.listDirectory();
1259 if (mDualPane) {
1260 Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1261 if (fragment != null && fragment instanceof FileDetailFragment) {
1262 FileDetailFragment detailFragment = (FileDetailFragment)fragment;
1263 detailFragment.listenForTransferProgress();
1264 detailFragment.updateFileDetails(false, false);
1265 }
1266 }
1267 }
1268
1269 @Override
1270 public void onServiceDisconnected(ComponentName component) {
1271 if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
1272 Log.d(TAG, "Download service disconnected");
1273 mDownloaderBinder = null;
1274 } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
1275 Log.d(TAG, "Upload service disconnected");
1276 mUploaderBinder = null;
1277 }
1278 }
1279 };
1280
1281
1282
1283 /**
1284 * Launch an intent to request the PIN code to the user before letting him use the app
1285 */
1286 private void requestPinCode() {
1287 boolean pinStart = false;
1288 SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
1289 pinStart = appPrefs.getBoolean("set_pincode", false);
1290 if (pinStart) {
1291 Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
1292 i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
1293 startActivity(i);
1294 }
1295 }
1296
1297
1298 @Override
1299 public void onSavedCertificate() {
1300 startSynchronization();
1301 }
1302
1303
1304 @Override
1305 public void onFailedSavingCertificate() {
1306 showDialog(DIALOG_CERT_NOT_SAVED);
1307 }
1308
1309
1310 /**
1311 * Updates the view associated to the activity after the finish of some operation over files
1312 * in the current account.
1313 *
1314 * @param operation Removal operation performed.
1315 * @param result Result of the removal.
1316 */
1317 @Override
1318 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
1319 if (operation instanceof RemoveFileOperation) {
1320 onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
1321
1322 } else if (operation instanceof RenameFileOperation) {
1323 onRenameFileOperationFinish((RenameFileOperation)operation, result);
1324
1325 } else if (operation instanceof SynchronizeFileOperation) {
1326 onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
1327 }
1328 }
1329
1330
1331 /**
1332 * Updates the view associated to the activity after the finish of an operation trying to remove a
1333 * file.
1334 *
1335 * @param operation Removal operation performed.
1336 * @param result Result of the removal.
1337 */
1338 private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
1339 dismissDialog(DIALOG_SHORT_WAIT);
1340 if (result.isSuccess()) {
1341 Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
1342 msg.show();
1343 OCFile removedFile = operation.getFile();
1344 if (mDualPane) {
1345 FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1346 if (details != null && removedFile.equals(details.getFile())) {
1347 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1348 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
1349 transaction.commit();
1350 }
1351 }
1352 if (mStorageManager.getFileById(removedFile.getParentId()).equals(mCurrentDir)) {
1353 mFileList.listDirectory();
1354 }
1355
1356 } else {
1357 Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG);
1358 msg.show();
1359 if (result.isSslRecoverableException()) {
1360 mLastSslUntrustedServerResult = result;
1361 showDialog(DIALOG_SSL_VALIDATOR);
1362 }
1363 }
1364 }
1365
1366 /**
1367 * Updates the view associated to the activity after the finish of an operation trying to rename a
1368 * file.
1369 *
1370 * @param operation Renaming operation performed.
1371 * @param result Result of the renaming.
1372 */
1373 private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
1374 dismissDialog(DIALOG_SHORT_WAIT);
1375 OCFile renamedFile = operation.getFile();
1376 if (result.isSuccess()) {
1377 if (mDualPane) {
1378 FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1379 if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
1380 ((FileDetailFragment) details).updateFileDetails(renamedFile, AccountUtils.getCurrentOwnCloudAccount(this));
1381 }
1382 }
1383 if (mStorageManager.getFileById(renamedFile.getParentId()).equals(mCurrentDir)) {
1384 mFileList.listDirectory();
1385 }
1386
1387 } else {
1388 if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
1389 Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
1390 msg.show();
1391 // TODO throw again the new rename dialog
1392 } else {
1393 Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
1394 msg.show();
1395 if (result.isSslRecoverableException()) {
1396 mLastSslUntrustedServerResult = result;
1397 showDialog(DIALOG_SSL_VALIDATOR);
1398 }
1399 }
1400 }
1401 }
1402
1403
1404 private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
1405 dismissDialog(DIALOG_SHORT_WAIT);
1406 OCFile syncedFile = operation.getLocalFile();
1407 if (!result.isSuccess()) {
1408 if (result.getCode() == ResultCode.SYNC_CONFLICT) {
1409 Intent i = new Intent(this, ConflictsResolveActivity.class);
1410 i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
1411 i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1412 startActivity(i);
1413
1414 } else {
1415 Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
1416 msg.show();
1417 }
1418
1419 } else {
1420 if (operation.transferWasRequested()) {
1421 mFileList.listDirectory();
1422 onTransferStateChanged(syncedFile, true, true);
1423
1424 } else {
1425 Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
1426 msg.show();
1427 }
1428 }
1429 }
1430
1431
1432 /**
1433 * {@inheritDoc}
1434 */
1435 @Override
1436 public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
1437 /*OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
1438 if (fileListFragment != null) {
1439 fileListFragment.listDirectory();
1440 }*/
1441 if (mDualPane) {
1442 FileFragment details = (FileFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1443 if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
1444 if (downloading || uploading) {
1445 ((FileDetailFragment)details).updateFileDetails(file, AccountUtils.getCurrentOwnCloudAccount(this));
1446 } else {
1447 ((FileDetailFragment)details).updateFileDetails(false, true);
1448 }
1449 }
1450 }
1451 }
1452
1453
1454 @Override
1455 public void showFragmentWithDetails(OCFile file) {
1456 if (mDualPane) {
1457 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1458 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
1459 transaction.commit();
1460
1461 } else {
1462 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
1463 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
1464 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
1465 showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS);
1466 startActivity(showDetailsIntent);
1467 }
1468 }
1469
1470 public void onDismiss(EditNameDialog dialog) {
1471 //dialog.dismiss();
1472 if (dialog.getResult()) {
1473 String newDirectoryName = dialog.getNewFilename().trim();
1474 Log.d(TAG, "'create directory' dialog dismissed with new name " + newDirectoryName);
1475 if (newDirectoryName.length() > 0) {
1476 String path;
1477 if (mCurrentDir == null) {
1478 // this is just a patch; we should ensure that mCurrentDir never is null
1479 if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
1480 OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
1481 mStorageManager.saveFile(file);
1482 }
1483 mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
1484 }
1485 path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
1486
1487 // Create directory
1488 path += newDirectoryName + OCFile.PATH_SEPARATOR;
1489 Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));
1490 thread.start();
1491
1492 showDialog(DIALOG_SHORT_WAIT);
1493 }
1494 }
1495 }
1496
1497 private void requestForDownload() {
1498 Account account = AccountUtils.getCurrentOwnCloudAccount(this);
1499 if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
1500 Intent i = new Intent(this, FileDownloader.class);
1501 i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
1502 i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
1503 startService(i);
1504 }
1505 }
1506
1507
1508 }
1509