358ae9eb51e113de4ff15679e5114c5a58bc8c56
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / FileDisplayActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 package com.owncloud.android.ui.activity;
20
21 import java.io.File;
22
23 import android.accounts.Account;
24 import android.app.AlertDialog;
25 import android.app.ProgressDialog;
26 import android.app.AlertDialog.Builder;
27 import android.app.Dialog;
28 import android.content.BroadcastReceiver;
29 import android.content.ComponentName;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.DialogInterface.OnClickListener;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.ServiceConnection;
37 import android.content.SharedPreferences;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageManager.NameNotFoundException;
40 import android.content.res.Resources.NotFoundException;
41 import android.database.Cursor;
42 import android.net.Uri;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.IBinder;
46 import android.preference.PreferenceManager;
47 import android.provider.MediaStore;
48 import android.support.v4.app.FragmentTransaction;
49 import android.util.Log;
50 import android.view.View;
51 import android.view.ViewGroup;
52 import android.widget.ArrayAdapter;
53 import android.widget.EditText;
54 import android.widget.TextView;
55 import android.widget.Toast;
56
57 import com.actionbarsherlock.app.ActionBar;
58 import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
59 import com.actionbarsherlock.app.SherlockFragmentActivity;
60 import com.actionbarsherlock.view.Menu;
61 import com.actionbarsherlock.view.MenuInflater;
62 import com.actionbarsherlock.view.MenuItem;
63 import com.actionbarsherlock.view.Window;
64 import com.owncloud.android.AccountUtils;
65 import com.owncloud.android.authenticator.AccountAuthenticator;
66 import com.owncloud.android.datamodel.DataStorageManager;
67 import com.owncloud.android.datamodel.FileDataStorageManager;
68 import com.owncloud.android.datamodel.OCFile;
69 import com.owncloud.android.files.services.FileDownloader;
70 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
71 import com.owncloud.android.files.services.FileObserverService;
72 import com.owncloud.android.files.services.FileUploader;
73 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
74 import com.owncloud.android.network.OwnCloudClientUtils;
75 import com.owncloud.android.operations.RemoteOperationResult;
76 import com.owncloud.android.syncadapter.FileSyncService;
77 import com.owncloud.android.ui.dialog.SslValidatorDialog;
78 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
79 import com.owncloud.android.ui.fragment.FileDetailFragment;
80 import com.owncloud.android.ui.fragment.OCFileListFragment;
81
82 import com.owncloud.android.R;
83 import eu.alefzero.webdav.WebdavClient;
84
85 /**
86 * Displays, what files the user has available in his ownCloud.
87 *
88 * @author Bartek Przybylski
89 *
90 */
91
92 public class FileDisplayActivity extends SherlockFragmentActivity implements
93 OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener {
94
95 private ArrayAdapter<String> mDirectories;
96 private OCFile mCurrentDir = null;
97 private OCFile mCurrentFile = null;
98
99 private DataStorageManager mStorageManager;
100 private SyncBroadcastReceiver mSyncBroadcastReceiver;
101 private UploadFinishReceiver mUploadFinishReceiver;
102 private DownloadFinishReceiver mDownloadFinishReceiver;
103 private FileDownloaderBinder mDownloaderBinder = null;
104 private FileUploaderBinder mUploaderBinder = null;
105 private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
106 private RemoteOperationResult mLastSslUntrustedServerResult = null;
107
108 private OCFileListFragment mFileList;
109
110 private boolean mDualPane;
111
112 private static final int DIALOG_SETUP_ACCOUNT = 0;
113 private static final int DIALOG_CREATE_DIR = 1;
114 private static final int DIALOG_ABOUT_APP = 2;
115 public static final int DIALOG_SHORT_WAIT = 3;
116 private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 4;
117 private static final int DIALOG_SSL_VALIDATOR = 5;
118 private static final int DIALOG_CERT_NOT_SAVED = 6;
119
120
121 private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
122 private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
123
124 private static final String TAG = "FileDisplayActivity";
125
126 @Override
127 public void onCreate(Bundle savedInstanceState) {
128 Log.d(getClass().toString(), "onCreate() start");
129 super.onCreate(savedInstanceState);
130
131 /// Load of parameters from received intent
132 mCurrentDir = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); // no check necessary, mCurrenDir == null if the parameter is not in the intent
133 Account account = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
134 if (account != null)
135 AccountUtils.setCurrentOwnCloudAccount(this, account.name);
136
137 /// Load of saved instance state: keep this always before initDataFromCurrentAccount()
138 if(savedInstanceState != null) {
139 // TODO - test if savedInstanceState should take precedence over file in the intent ALWAYS (now), NEVER, or SOME TIMES
140 mCurrentDir = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
141 }
142
143 if (!AccountUtils.accountsAreSetup(this)) {
144 /// no account available: FORCE ACCOUNT CREATION
145 mStorageManager = null;
146 createFirstAccount();
147
148 } else { /// at least an account is available
149
150 initDataFromCurrentAccount(); // it checks mCurrentDir and mCurrentFile with the current account
151
152 }
153
154 mUploadConnection = new ListServiceConnection();
155 mDownloadConnection = new ListServiceConnection();
156 bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
157 bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
158
159 // PIN CODE request ; best location is to decide, let's try this first
160 if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
161 requestPinCode();
162 }
163
164 // file observer
165 Intent observer_intent = new Intent(this, FileObserverService.class);
166 observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);
167 startService(observer_intent);
168
169
170 /// USER INTERFACE
171 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
172
173 // Drop-down navigation
174 mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
175 OCFile currFile = mCurrentDir;
176 while(currFile != null && currFile.getFileName() != OCFile.PATH_SEPARATOR) {
177 mDirectories.add(currFile.getFileName());
178 currFile = mStorageManager.getFileById(currFile.getParentId());
179 }
180 mDirectories.add(OCFile.PATH_SEPARATOR);
181
182 // Inflate and set the layout view
183 setContentView(R.layout.files);
184 mFileList = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
185 mDualPane = (findViewById(R.id.file_details_container) != null);
186 if (mDualPane) {
187 initFileDetailsInDualPane();
188 }
189
190 // Action bar setup
191 ActionBar actionBar = getSupportActionBar();
192 actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
193 actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getParentId() != 0);
194 actionBar.setDisplayShowTitleEnabled(false);
195 actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
196 actionBar.setListNavigationCallbacks(mDirectories, this);
197 setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to workaround bug in its implementation
198
199 Log.d(getClass().toString(), "onCreate() end");
200 }
201
202
203 /**
204 * Launches the account creation activity. To use when no ownCloud account is available
205 */
206 private void createFirstAccount() {
207 Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
208 intent.putExtra(android.provider.Settings.EXTRA_AUTHORITIES, new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
209 startActivity(intent); // the new activity won't be created until this.onStart() and this.onResume() are finished;
210 }
211
212
213 /**
214 * Load of state dependent of the existence of an ownCloud account
215 */
216 private void initDataFromCurrentAccount() {
217 /// Storage manager initialization - access to local database
218 mStorageManager = new FileDataStorageManager(
219 AccountUtils.getCurrentOwnCloudAccount(this),
220 getContentResolver());
221
222 /// Check if mCurrentDir is a directory
223 if(mCurrentDir != null && !mCurrentDir.isDirectory()) {
224 mCurrentFile = mCurrentDir;
225 mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());
226 }
227
228 /// Check if mCurrentDir and mCurrentFile are in the current account, and update them
229 if (mCurrentDir != null) {
230 mCurrentDir = mStorageManager.getFileByPath(mCurrentDir.getRemotePath()); // mCurrentDir == null if it is not in the current account
231 }
232 if (mCurrentFile != null) {
233 if (mCurrentFile.fileExists()) {
234 mCurrentFile = mStorageManager.getFileByPath(mCurrentFile.getRemotePath()); // mCurrentFile == null if it is not in the current account
235 } // 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
236 }
237
238 /// Default to root if mCurrentDir was not found
239 if (mCurrentDir == null) {
240 mCurrentDir = mStorageManager.getFileByPath("/"); // will be NULL if the database was never synchronized
241 }
242 }
243
244
245 private void initFileDetailsInDualPane() {
246 if (mDualPane && getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG) == null) {
247 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
248 if (mCurrentFile != null) {
249 transaction.replace(R.id.file_details_container, new FileDetailFragment(mCurrentFile, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG); // empty FileDetailFragment
250 mCurrentFile = null;
251 } else {
252 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG); // empty FileDetailFragment
253 }
254 transaction.commit();
255 }
256 }
257
258
259 @Override
260 public void onDestroy() {
261 super.onDestroy();
262 if (mDownloadConnection != null)
263 unbindService(mDownloadConnection);
264 if (mUploadConnection != null)
265 unbindService(mUploadConnection);
266 }
267
268
269 @Override
270 public boolean onCreateOptionsMenu(Menu menu) {
271 MenuInflater inflater = getSherlock().getMenuInflater();
272 inflater.inflate(R.menu.menu, menu);
273 return true;
274 }
275
276 @Override
277 public boolean onOptionsItemSelected(MenuItem item) {
278 boolean retval = true;
279 switch (item.getItemId()) {
280 case R.id.createDirectoryItem: {
281 showDialog(DIALOG_CREATE_DIR);
282 break;
283 }
284 case R.id.startSync: {
285 startSynchronization();
286 break;
287 }
288 case R.id.action_upload: {
289 showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
290 break;
291 }
292 case R.id.action_settings: {
293 Intent settingsIntent = new Intent(this, Preferences.class);
294 startActivity(settingsIntent);
295 break;
296 }
297 case R.id.about_app : {
298 showDialog(DIALOG_ABOUT_APP);
299 break;
300 }
301 case android.R.id.home: {
302 if(mCurrentDir != null && mCurrentDir.getParentId() != 0){
303 onBackPressed();
304 }
305 break;
306 }
307 default:
308 retval = super.onOptionsItemSelected(item);
309 }
310 return retval;
311 }
312
313 private void startSynchronization() {
314 ContentResolver.cancelSync(null, AccountAuthenticator.AUTH_TOKEN_TYPE); // cancel the current synchronizations of any ownCloud account
315 Bundle bundle = new Bundle();
316 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
317 ContentResolver.requestSync(
318 AccountUtils.getCurrentOwnCloudAccount(this),
319 AccountAuthenticator.AUTH_TOKEN_TYPE, bundle);
320 }
321
322
323 @Override
324 public boolean onNavigationItemSelected(int itemPosition, long itemId) {
325 int i = itemPosition;
326 while (i-- != 0) {
327 onBackPressed();
328 }
329 // the next operation triggers a new call to this method, but it's necessary to
330 // ensure that the name exposed in the action bar is the current directory when the
331 // user selected it in the navigation list
332 if (itemPosition != 0)
333 getSupportActionBar().setSelectedNavigationItem(0);
334 return true;
335 }
336
337 /**
338 * Called, when the user selected something for uploading
339 */
340 public void onActivityResult(int requestCode, int resultCode, Intent data) {
341
342 if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && resultCode == RESULT_OK) {
343 requestSimpleUpload(data);
344
345 } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && resultCode == RESULT_OK) {
346 requestMultipleUpload(data);
347
348 }
349 }
350
351 private void requestMultipleUpload(Intent data) {
352 String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
353 if (filePaths != null) {
354 String[] remotePaths = new String[filePaths.length];
355 String remotePathBase = "";
356 for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
357 remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
358 }
359 if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))
360 remotePathBase += OCFile.PATH_SEPARATOR;
361 for (int j = 0; j< remotePaths.length; j++) {
362 remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
363 }
364
365 Intent i = new Intent(this, FileUploader.class);
366 i.putExtra(FileUploader.KEY_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
367 i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
368 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
369 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
370 startService(i);
371
372 } else {
373 Log.d("FileDisplay", "User clicked on 'Update' with no selection");
374 Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);
375 t.show();
376 return;
377 }
378 }
379
380
381 private void requestSimpleUpload(Intent data) {
382 String filepath = null;
383 try {
384 Uri selectedImageUri = data.getData();
385
386 String filemanagerstring = selectedImageUri.getPath();
387 String selectedImagePath = getPath(selectedImageUri);
388
389 if (selectedImagePath != null)
390 filepath = selectedImagePath;
391 else
392 filepath = filemanagerstring;
393
394 } catch (Exception e) {
395 Log.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
396 e.printStackTrace();
397
398 } finally {
399 if (filepath == null) {
400 Log.e("FileDisplay", "Couldnt resolve path to file");
401 Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
402 t.show();
403 return;
404 }
405 }
406
407 Intent i = new Intent(this, FileUploader.class);
408 i.putExtra(FileUploader.KEY_ACCOUNT,
409 AccountUtils.getCurrentOwnCloudAccount(this));
410 String remotepath = new String();
411 for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
412 remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
413 }
414 if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
415 remotepath += OCFile.PATH_SEPARATOR;
416 remotepath += new File(filepath).getName();
417
418 i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
419 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
420 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
421 startService(i);
422 }
423
424
425 @Override
426 public void onBackPressed() {
427 if (mDirectories.getCount() <= 1) {
428 finish();
429 return;
430 }
431 popDirname();
432 mFileList.onNavigateUp();
433 mCurrentDir = mFileList.getCurrentFile();
434
435 if (mDualPane) {
436 // Resets the FileDetailsFragment on Tablets so that it always displays
437 FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
438 if (fileDetails != null && !fileDetails.isEmpty()) {
439 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
440 transaction.remove(fileDetails);
441 transaction.add(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG);
442 transaction.commit();
443 }
444 }
445
446 if(mCurrentDir.getParentId() == 0){
447 ActionBar actionBar = getSupportActionBar();
448 actionBar.setDisplayHomeAsUpEnabled(false);
449 }
450 }
451
452 @Override
453 protected void onSaveInstanceState(Bundle outState) {
454 // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
455 Log.d(getClass().toString(), "onSaveInstanceState() start");
456 super.onSaveInstanceState(outState);
457 outState.putParcelable(FileDetailFragment.EXTRA_FILE, mCurrentDir);
458 if (mDualPane) {
459 FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
460 if (fragment != null) {
461 OCFile file = fragment.getDisplayedFile();
462 if (file != null) {
463 outState.putParcelable(FileDetailFragment.EXTRA_FILE, file);
464 }
465 }
466 }
467 Log.d(getClass().toString(), "onSaveInstanceState() end");
468 }
469
470 @Override
471 protected void onResume() {
472 Log.d(getClass().toString(), "onResume() start");
473 super.onResume();
474
475 if (AccountUtils.accountsAreSetup(this)) {
476
477 if (mStorageManager == null) {
478 // this is necessary for handling the come back to FileDisplayActivity when the first ownCloud account is created
479 initDataFromCurrentAccount();
480 if (mDualPane) {
481 initFileDetailsInDualPane();
482 }
483 }
484
485 // Listen for sync messages
486 IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);
487 mSyncBroadcastReceiver = new SyncBroadcastReceiver();
488 registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
489
490 // Listen for upload messages
491 IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
492 mUploadFinishReceiver = new UploadFinishReceiver();
493 registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
494
495 // Listen for download messages
496 IntentFilter downloadIntentFilter = new IntentFilter(FileDownloader.DOWNLOAD_FINISH_MESSAGE);
497 mDownloadFinishReceiver = new DownloadFinishReceiver();
498 registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
499
500 // List current directory
501 mFileList.listDirectory(mCurrentDir); // TODO we should find the way to avoid the need of this (maybe it's not necessary yet; to check)
502
503 } else {
504
505 mStorageManager = null; // an invalid object will be there if all the ownCloud accounts are removed
506 showDialog(DIALOG_SETUP_ACCOUNT);
507
508 }
509 Log.d(getClass().toString(), "onResume() end");
510 }
511
512
513 @Override
514 protected void onPause() {
515 Log.d(getClass().toString(), "onPause() start");
516 super.onPause();
517 if (mSyncBroadcastReceiver != null) {
518 unregisterReceiver(mSyncBroadcastReceiver);
519 mSyncBroadcastReceiver = null;
520 }
521 if (mUploadFinishReceiver != null) {
522 unregisterReceiver(mUploadFinishReceiver);
523 mUploadFinishReceiver = null;
524 }
525 if (mDownloadFinishReceiver != null) {
526 unregisterReceiver(mDownloadFinishReceiver);
527 mDownloadFinishReceiver = null;
528 }
529 if (!AccountUtils.accountsAreSetup(this)) {
530 dismissDialog(DIALOG_SETUP_ACCOUNT);
531 }
532
533 Log.d(getClass().toString(), "onPause() end");
534 }
535
536
537 @Override
538 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
539 if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {
540 ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
541 }
542 }
543
544
545 @Override
546 protected Dialog onCreateDialog(int id) {
547 Dialog dialog = null;
548 AlertDialog.Builder builder;
549 switch (id) {
550 case DIALOG_SETUP_ACCOUNT: {
551 builder = new AlertDialog.Builder(this);
552 builder.setTitle(R.string.main_tit_accsetup);
553 builder.setMessage(R.string.main_wrn_accsetup);
554 builder.setCancelable(false);
555 builder.setPositiveButton(android.R.string.ok, new OnClickListener() {
556 public void onClick(DialogInterface dialog, int which) {
557 createFirstAccount();
558 dialog.dismiss();
559 }
560 });
561 builder.setNegativeButton(R.string.common_exit, new OnClickListener() {
562 public void onClick(DialogInterface dialog, int which) {
563 dialog.dismiss();
564 finish();
565 }
566 });
567 //builder.setNegativeButton(android.R.string.cancel, this);
568 dialog = builder.create();
569 break;
570 }
571 case DIALOG_ABOUT_APP: {
572 builder = new AlertDialog.Builder(this);
573 builder.setTitle(getString(R.string.about_title));
574 PackageInfo pkg;
575 try {
576 pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
577 builder.setMessage(String.format(getString(R.string.about_message), pkg.versionName));
578 builder.setIcon(android.R.drawable.ic_menu_info_details);
579 dialog = builder.create();
580 } catch (NameNotFoundException e) {
581 builder = null;
582 dialog = null;
583 Log.e(TAG, "Error while showing about dialog", e);
584 }
585 break;
586 }
587 case DIALOG_CREATE_DIR: {
588 builder = new Builder(this);
589 final EditText dirNameInput = new EditText(getBaseContext());
590 builder.setView(dirNameInput);
591 builder.setTitle(R.string.uploader_info_dirname);
592 int typed_color = getResources().getColor(R.color.setup_text_typed);
593 dirNameInput.setTextColor(typed_color);
594 builder.setPositiveButton(android.R.string.ok,
595 new OnClickListener() {
596 public void onClick(DialogInterface dialog, int which) {
597 String directoryName = dirNameInput.getText().toString();
598 if (directoryName.trim().length() == 0) {
599 dialog.cancel();
600 return;
601 }
602
603 // Figure out the path where the dir needs to be created
604 String path;
605 if (mCurrentDir == null) {
606 // this is just a patch; we should ensure that mCurrentDir never is null
607 if (!mStorageManager.fileExists(OCFile.PATH_SEPARATOR)) {
608 OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
609 mStorageManager.saveFile(file);
610 }
611 mCurrentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
612 }
613 path = FileDisplayActivity.this.mCurrentDir.getRemotePath();
614
615 // Create directory
616 path += directoryName + OCFile.PATH_SEPARATOR;
617 Thread thread = new Thread(new DirectoryCreator(path, AccountUtils.getCurrentOwnCloudAccount(FileDisplayActivity.this), new Handler()));
618 thread.start();
619
620 dialog.dismiss();
621
622 showDialog(DIALOG_SHORT_WAIT);
623 }
624 });
625 builder.setNegativeButton(R.string.common_cancel,
626 new OnClickListener() {
627 public void onClick(DialogInterface dialog, int which) {
628 dialog.cancel();
629 }
630 });
631 dialog = builder.create();
632 break;
633 }
634 case DIALOG_SHORT_WAIT: {
635 ProgressDialog working_dialog = new ProgressDialog(this);
636 working_dialog.setMessage(getResources().getString(
637 R.string.wait_a_moment));
638 working_dialog.setIndeterminate(true);
639 working_dialog.setCancelable(false);
640 dialog = working_dialog;
641 break;
642 }
643 case DIALOG_CHOOSE_UPLOAD_SOURCE: {
644 final String [] items = { getString(R.string.actionbar_upload_files),
645 getString(R.string.actionbar_upload_from_apps) };
646 builder = new AlertDialog.Builder(this);
647 builder.setTitle(R.string.actionbar_upload);
648 builder.setItems(items, new DialogInterface.OnClickListener() {
649 public void onClick(DialogInterface dialog, int item) {
650 if (item == 0) {
651 //if (!mDualPane) {
652 Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
653 startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
654 //} else {
655 // TODO create and handle new fragment LocalFileListFragment
656 //}
657 } else if (item == 1) {
658 Intent action = new Intent(Intent.ACTION_GET_CONTENT);
659 action = action.setType("*/*")
660 .addCategory(Intent.CATEGORY_OPENABLE);
661 startActivityForResult(
662 Intent.createChooser(action, getString(R.string.upload_chooser_title)),
663 ACTION_SELECT_CONTENT_FROM_APPS);
664 }
665 }
666 });
667 dialog = builder.create();
668 break;
669 }
670 case DIALOG_SSL_VALIDATOR: {
671 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
672 break;
673 }
674 case DIALOG_CERT_NOT_SAVED: {
675 builder = new AlertDialog.Builder(this);
676 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
677 builder.setCancelable(false);
678 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
679 @Override
680 public void onClick(DialogInterface dialog, int which) {
681 dialog.dismiss();
682 };
683 });
684 dialog = builder.create();
685 break;
686 }
687 default:
688 dialog = null;
689 }
690
691 return dialog;
692 }
693
694
695 /**
696 * Translates a content URI of an image to a physical path
697 * on the disk
698 * @param uri The URI to resolve
699 * @return The path to the image or null if it could not be found
700 */
701 public String getPath(Uri uri) {
702 String[] projection = { MediaStore.Images.Media.DATA };
703 Cursor cursor = managedQuery(uri, projection, null, null, null);
704 if (cursor != null) {
705 int column_index = cursor
706 .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
707 cursor.moveToFirst();
708 return cursor.getString(column_index);
709 }
710 return null;
711 }
712
713 /**
714 * Pushes a directory to the drop down list
715 * @param directory to push
716 * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
717 */
718 public void pushDirname(OCFile directory) {
719 if(!directory.isDirectory()){
720 throw new IllegalArgumentException("Only directories may be pushed!");
721 }
722 mDirectories.insert(directory.getFileName(), 0);
723 mCurrentDir = directory;
724 }
725
726 /**
727 * Pops a directory name from the drop down list
728 * @return True, unless the stack is empty
729 */
730 public boolean popDirname() {
731 mDirectories.remove(mDirectories.getItem(0));
732 return !mDirectories.isEmpty();
733 }
734
735 private class DirectoryCreator implements Runnable {
736 private String mTargetPath;
737 private Account mAccount;
738 private Handler mHandler;
739
740 public DirectoryCreator(String targetPath, Account account, Handler handler) {
741 mTargetPath = targetPath;
742 mAccount = account;
743 mHandler = handler;
744 }
745
746 @Override
747 public void run() {
748 WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
749 boolean created = wdc.createDirectory(mTargetPath);
750 if (created) {
751 mHandler.post(new Runnable() {
752 @Override
753 public void run() {
754 dismissDialog(DIALOG_SHORT_WAIT);
755
756 // Save new directory in local database
757 OCFile newDir = new OCFile(mTargetPath);
758 newDir.setMimetype("DIR");
759 newDir.setParentId(mCurrentDir.getFileId());
760 mStorageManager.saveFile(newDir);
761
762 // Display the new folder right away
763 mFileList.listDirectory();
764 }
765 });
766
767 } else {
768 mHandler.post(new Runnable() {
769 @Override
770 public void run() {
771 dismissDialog(DIALOG_SHORT_WAIT);
772 try {
773 Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
774 msg.show();
775
776 } catch (NotFoundException e) {
777 Log.e(TAG, "Error while trying to show fail message " , e);
778 }
779 }
780 });
781 }
782 }
783
784 }
785
786 // Custom array adapter to override text colors
787 private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
788
789 public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
790 super(ctx, view);
791 }
792
793 public View getView(int position, View convertView, ViewGroup parent) {
794 View v = super.getView(position, convertView, parent);
795
796 ((TextView) v).setTextColor(getResources().getColorStateList(
797 android.R.color.white));
798 return v;
799 }
800
801 public View getDropDownView(int position, View convertView,
802 ViewGroup parent) {
803 View v = super.getDropDownView(position, convertView, parent);
804
805 ((TextView) v).setTextColor(getResources().getColorStateList(
806 android.R.color.white));
807
808 return v;
809 }
810
811 }
812
813 private class SyncBroadcastReceiver extends BroadcastReceiver {
814
815 /**
816 * {@link BroadcastReceiver} to enable syncing feedback in UI
817 */
818 @Override
819 public void onReceive(Context context, Intent intent) {
820 boolean inProgress = intent.getBooleanExtra(
821 FileSyncService.IN_PROGRESS, false);
822 String accountName = intent
823 .getStringExtra(FileSyncService.ACCOUNT_NAME);
824
825 Log.d("FileDisplay", "sync of account " + accountName
826 + " is in_progress: " + inProgress);
827
828 if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name)) {
829
830 String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
831
832 boolean fillBlankRoot = false;
833 if (mCurrentDir == null) {
834 mCurrentDir = mStorageManager.getFileByPath("/");
835 fillBlankRoot = (mCurrentDir != null);
836 }
837
838 if ((synchFolderRemotePath != null && mCurrentDir != null && (mCurrentDir.getRemotePath().equals(synchFolderRemotePath)))
839 || fillBlankRoot ) {
840 if (!fillBlankRoot)
841 mCurrentDir = getStorageManager().getFileByPath(synchFolderRemotePath);
842 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager()
843 .findFragmentById(R.id.fileList);
844 if (fileListFragment != null) {
845 fileListFragment.listDirectory(mCurrentDir);
846 }
847 }
848
849 setSupportProgressBarIndeterminateVisibility(inProgress);
850
851 }
852
853 RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
854 if (synchResult != null) {
855 if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
856 mLastSslUntrustedServerResult = synchResult;
857 showDialog(DIALOG_SSL_VALIDATOR);
858 }
859 }
860 }
861 }
862
863
864 private class UploadFinishReceiver extends BroadcastReceiver {
865 /**
866 * Once the file upload has finished -> update view
867 * @author David A. Velasco
868 * {@link BroadcastReceiver} to enable upload feedback in UI
869 */
870 @Override
871 public void onReceive(Context context, Intent intent) {
872 long parentDirId = intent.getLongExtra(FileUploader.EXTRA_PARENT_DIR_ID, -1);
873 OCFile parentDir = mStorageManager.getFileById(parentDirId);
874 String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
875
876 if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name) &&
877 parentDir != null &&
878 ( (mCurrentDir == null && parentDir.getFileName().equals("/")) ||
879 parentDir.equals(mCurrentDir)
880 )
881 ) {
882 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
883 if (fileListFragment != null) {
884 fileListFragment.listDirectory();
885 }
886 }
887 }
888
889 }
890
891
892 /**
893 * Once the file download has finished -> update view
894 */
895 private class DownloadFinishReceiver extends BroadcastReceiver {
896 @Override
897 public void onReceive(Context context, Intent intent) {
898 String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
899 String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
900
901 if (accountName.equals(AccountUtils.getCurrentOwnCloudAccount(context).name) &&
902 mCurrentDir != null && mCurrentDir.getFileId() == mStorageManager.getFileByPath(downloadedRemotePath).getParentId()) {
903 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
904 if (fileListFragment != null) {
905 fileListFragment.listDirectory();
906 }
907 }
908 }
909 }
910
911
912
913
914 /**
915 * {@inheritDoc}
916 */
917 @Override
918 public DataStorageManager getStorageManager() {
919 return mStorageManager;
920 }
921
922
923 /**
924 * {@inheritDoc}
925 */
926 @Override
927 public void onDirectoryClick(OCFile directory) {
928 pushDirname(directory);
929 ActionBar actionBar = getSupportActionBar();
930 actionBar.setDisplayHomeAsUpEnabled(true);
931
932 if (mDualPane) {
933 // Resets the FileDetailsFragment on Tablets so that it always displays
934 FileDetailFragment fileDetails = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
935 if (fileDetails != null && !fileDetails.isEmpty()) {
936 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
937 transaction.remove(fileDetails);
938 transaction.add(R.id.file_details_container, new FileDetailFragment(null, null), FileDetailFragment.FTAG);
939 transaction.commit();
940 }
941 }
942 }
943
944
945 /**
946 * {@inheritDoc}
947 */
948 @Override
949 public void onFileClick(OCFile file) {
950
951 // If we are on a large device -> update fragment
952 if (mDualPane) {
953 // buttons in the details view are problematic when trying to reuse an existing fragment; create always a new one solves some of them, BUT no all; downloads are 'dangerous'
954 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
955 transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
956 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
957 transaction.commit();
958
959 } else { // small or medium screen device -> new Activity
960 Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
961 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
962 showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
963 startActivity(showDetailsIntent);
964 }
965 }
966
967
968 /**
969 * {@inheritDoc}
970 */
971 @Override
972 public OCFile getInitialDirectory() {
973 return mCurrentDir;
974 }
975
976
977 /**
978 * {@inheritDoc}
979 */
980 @Override
981 public void onFileStateChanged() {
982 OCFileListFragment fileListFragment = (OCFileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
983 if (fileListFragment != null) {
984 fileListFragment.listDirectory();
985 }
986 }
987
988
989 /**
990 * {@inheritDoc}
991 */
992 @Override
993 public FileDownloaderBinder getFileDownloaderBinder() {
994 return mDownloaderBinder;
995 }
996
997
998 /**
999 * {@inheritDoc}
1000 */
1001 @Override
1002 public FileUploaderBinder getFileUploaderBinder() {
1003 return mUploaderBinder;
1004 }
1005
1006
1007 /** Defines callbacks for service binding, passed to bindService() */
1008 private class ListServiceConnection implements ServiceConnection {
1009
1010 @Override
1011 public void onServiceConnected(ComponentName component, IBinder service) {
1012 if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
1013 Log.d(TAG, "Download service connected");
1014 mDownloaderBinder = (FileDownloaderBinder) service;
1015 } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
1016 Log.d(TAG, "Upload service connected");
1017 mUploaderBinder = (FileUploaderBinder) service;
1018 } else {
1019 return;
1020 }
1021 // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
1022 if (mFileList != null)
1023 mFileList.listDirectory();
1024 if (mDualPane) {
1025 FileDetailFragment fragment = (FileDetailFragment) getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
1026 if (fragment != null)
1027 fragment.updateFileDetails();
1028 }
1029 }
1030
1031 @Override
1032 public void onServiceDisconnected(ComponentName component) {
1033 if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
1034 Log.d(TAG, "Download service disconnected");
1035 mDownloaderBinder = null;
1036 } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
1037 Log.d(TAG, "Upload service disconnected");
1038 mUploaderBinder = null;
1039 }
1040 }
1041 };
1042
1043
1044
1045 /**
1046 * Launch an intent to request the PIN code to the user before letting him use the app
1047 */
1048 private void requestPinCode() {
1049 boolean pinStart = false;
1050 SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
1051 pinStart = appPrefs.getBoolean("set_pincode", false);
1052 if (pinStart) {
1053 Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
1054 i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
1055 startActivity(i);
1056 }
1057 }
1058
1059
1060 @Override
1061 public void onSavedCertificate() {
1062 startSynchronization();
1063 }
1064
1065
1066 @Override
1067 public void onFailedSavingCertificate() {
1068 showDialog(DIALOG_CERT_NOT_SAVED);
1069 }
1070
1071
1072 }