1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   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. 
   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. 
  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/>. 
  19 package com
.owncloud
.android
.ui
.activity
; 
  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
; 
  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
.FileUploader
; 
  72 import com
.owncloud
.android
.network
.OwnCloudClientUtils
; 
  73 import com
.owncloud
.android
.syncadapter
.FileSyncService
; 
  74 import com
.owncloud
.android
.ui
.fragment
.FileDetailFragment
; 
  75 import com
.owncloud
.android
.ui
.fragment
.OCFileListFragment
; 
  77 import com
.owncloud
.android
.R
; 
  78 import eu
.alefzero
.webdav
.WebdavClient
; 
  81  * Displays, what files the user has available in his ownCloud. 
  83  * @author Bartek Przybylski 
  87 public class FileDisplayActivity 
extends SherlockFragmentActivity 
implements 
  88     OCFileListFragment
.ContainerActivity
, FileDetailFragment
.ContainerActivity
, OnNavigationListener 
{ 
  90     private ArrayAdapter
<String
> mDirectories
; 
  91     private OCFile mCurrentDir
; 
  93     private DataStorageManager mStorageManager
; 
  94     private SyncBroadcastReceiver mSyncBroadcastReceiver
; 
  95     private UploadFinishReceiver mUploadFinishReceiver
; 
  96     private DownloadFinishReceiver mDownloadFinishReceiver
; 
  97     private FileDownloaderBinder mDownloaderBinder 
= null
; 
  99     private OCFileListFragment mFileList
; 
 101     private boolean mDualPane
; 
 103     private static final int DIALOG_SETUP_ACCOUNT 
= 0; 
 104     private static final int DIALOG_CREATE_DIR 
= 1; 
 105     private static final int DIALOG_ABOUT_APP 
= 2; 
 106     public static final int DIALOG_SHORT_WAIT 
= 3; 
 107     private static final int DIALOG_CHOOSE_UPLOAD_SOURCE 
= 4; 
 109     private static final int ACTION_SELECT_CONTENT_FROM_APPS 
= 1; 
 110     private static final int ACTION_SELECT_MULTIPLE_FILES 
= 2; 
 112     private static final String TAG 
= "FileDisplayActivity"; 
 115     public void onCreate(Bundle savedInstanceState
) { 
 116         Log
.d(getClass().toString(), "onCreate() start"); 
 117         super.onCreate(savedInstanceState
); 
 119         /// saved instance state: keep this always before initDataFromCurrentAccount() 
 120         if(savedInstanceState 
!= null
) { 
 121             mCurrentDir 
= savedInstanceState
.getParcelable(FileDetailFragment
.EXTRA_FILE
); 
 124         if (!AccountUtils
.accountsAreSetup(this)) { 
 125             /// no account available: FORCE ACCOUNT CREATION 
 126             mStorageManager 
= null
; 
 127             createFirstAccount(); 
 129         } else {    /// at least an account is available 
 131             bindService(new Intent(this, FileDownloader
.class), mConnection
, Context
.BIND_AUTO_CREATE
); 
 132             initDataFromCurrentAccount(); 
 136         // PIN CODE request ;  best location is to decide, let's try this first 
 137         if (getIntent().getAction() != null 
&& getIntent().getAction().equals(Intent
.ACTION_MAIN
) && savedInstanceState 
== null
) { 
 142         /*Intent observer_intent = new Intent(this, FileObserverService.class); 
 143         observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST); 
 144         startService(observer_intent); 
 148         requestWindowFeature(Window
.FEATURE_INDETERMINATE_PROGRESS
); 
 150         // Drop-down navigation  
 151         mDirectories 
= new CustomArrayAdapter
<String
>(this, R
.layout
.sherlock_spinner_dropdown_item
); 
 152         OCFile currFile 
= mCurrentDir
; 
 153         while(currFile 
!= null 
&& currFile
.getFileName() != OCFile
.PATH_SEPARATOR
) { 
 154             mDirectories
.add(currFile
.getFileName()); 
 155             currFile 
= mStorageManager
.getFileById(currFile
.getParentId()); 
 157         mDirectories
.add(OCFile
.PATH_SEPARATOR
); 
 159         // Inflate and set the layout view 
 160         setContentView(R
.layout
.files
);     
 161         mFileList 
= (OCFileListFragment
) getSupportFragmentManager().findFragmentById(R
.id
.fileList
); 
 162         mDualPane 
= (findViewById(R
.id
.file_details_container
) != null
); 
 163         if (mDualPane 
&& getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
) == null
) { 
 164             FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 165             transaction
.replace(R
.id
.file_details_container
, new FileDetailFragment(null
, null
)); // empty FileDetailFragment 
 166             transaction
.commit(); 
 170         ActionBar actionBar 
= getSupportActionBar(); 
 171         actionBar
.setHomeButtonEnabled(true
);   // mandatory since Android ICS, according to the official documentation 
 172         actionBar
.setDisplayHomeAsUpEnabled(mCurrentDir 
!= null 
&& mCurrentDir
.getParentId() != 0); 
 173         actionBar
.setDisplayShowTitleEnabled(false
); 
 174         actionBar
.setNavigationMode(ActionBar
.NAVIGATION_MODE_LIST
); 
 175         actionBar
.setListNavigationCallbacks(mDirectories
, this); 
 176         setSupportProgressBarIndeterminateVisibility(false
);        // always AFTER setContentView(...) ; to workaround bug in its implementation 
 178         Log
.d(getClass().toString(), "onCreate() end"); 
 183      * Launches the account creation activity. To use when no ownCloud account is available 
 185     private void createFirstAccount() { 
 186         Intent intent 
= new Intent(android
.provider
.Settings
.ACTION_ADD_ACCOUNT
); 
 187         intent
.putExtra(android
.provider
.Settings
.EXTRA_AUTHORITIES
, new String
[] { AccountAuthenticator
.AUTH_TOKEN_TYPE 
}); 
 188         startActivity(intent
);  // the new activity won't be created until this.onStart() and this.onResume() are finished; 
 193      *  Load of state dependent of the existence of an ownCloud account 
 195     private void initDataFromCurrentAccount() { 
 196         /// Storage manager initialization - access to local database 
 197         mStorageManager 
= new FileDataStorageManager( 
 198                 AccountUtils
.getCurrentOwnCloudAccount(this), 
 199                 getContentResolver()); 
 201         /// State recovery - CURRENT DIRECTORY ; priority: 1/ getIntent(), 2/ savedInstanceState (in onCreate()), 3/ root dir 
 202         if(getIntent().hasExtra(FileDetailFragment
.EXTRA_FILE
)) { 
 203             mCurrentDir 
= (OCFile
) getIntent().getParcelableExtra(FileDetailFragment
.EXTRA_FILE
); 
 204             if(mCurrentDir 
!= null 
&& !mCurrentDir
.isDirectory()){ 
 205                 mCurrentDir 
= mStorageManager
.getFileById(mCurrentDir
.getParentId()); 
 207             // clear intent extra, so rotating the screen will not return us to this directory 
 208             getIntent().removeExtra(FileDetailFragment
.EXTRA_FILE
); 
 210         if (mCurrentDir 
== null
) 
 211             mCurrentDir 
= mStorageManager
.getFileByPath("/");   // this will return NULL if the database has not ever synchronized 
 216     public void onDestroy() { 
 218         unbindService(mConnection
); 
 223     public boolean onCreateOptionsMenu(Menu menu
) { 
 224         MenuInflater inflater 
= getSherlock().getMenuInflater(); 
 225             inflater
.inflate(R
.menu
.menu
, menu
); 
 230     public boolean onOptionsItemSelected(MenuItem item
) { 
 231         boolean retval 
= true
; 
 232         switch (item
.getItemId()) { 
 233             case R
.id
.createDirectoryItem
: { 
 234                 showDialog(DIALOG_CREATE_DIR
); 
 237             case R
.id
.startSync
: { 
 238                 ContentResolver
.cancelSync(null
, AccountAuthenticator
.AUTH_TOKEN_TYPE
);   // cancel the current synchronizations of any ownCloud account 
 239                 Bundle bundle 
= new Bundle(); 
 240                 bundle
.putBoolean(ContentResolver
.SYNC_EXTRAS_MANUAL
, true
); 
 241                 ContentResolver
.requestSync( 
 242                         AccountUtils
.getCurrentOwnCloudAccount(this), 
 243                         AccountAuthenticator
.AUTH_TOKEN_TYPE
, bundle
); 
 246             case R
.id
.action_upload
: { 
 247                 showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE
); 
 250             case R
.id
.action_settings
: { 
 251                 Intent settingsIntent 
= new Intent(this, Preferences
.class); 
 252                 startActivity(settingsIntent
); 
 255             case R
.id
.about_app 
: { 
 256                 showDialog(DIALOG_ABOUT_APP
); 
 259             case android
.R
.id
.home
: { 
 260                 if(mCurrentDir 
!= null 
&& mCurrentDir
.getParentId() != 0){ 
 272     public boolean onNavigationItemSelected(int itemPosition
, long itemId
) { 
 273         int i 
= itemPosition
; 
 277         // the next operation triggers a new call to this method, but it's necessary to  
 278         // ensure that the name exposed in the action bar is the current directory when the  
 279         // user selected it in the navigation list 
 280         if (itemPosition 
!= 0) 
 281             getSupportActionBar().setSelectedNavigationItem(0); 
 286      * Called, when the user selected something for uploading 
 288     public void onActivityResult(int requestCode
, int resultCode
, Intent data
) { 
 290         if (requestCode 
== ACTION_SELECT_CONTENT_FROM_APPS 
&& resultCode 
== RESULT_OK
) { 
 291             requestSimpleUpload(data
); 
 293         } else if (requestCode 
== ACTION_SELECT_MULTIPLE_FILES 
&& resultCode 
== RESULT_OK
) { 
 294             requestMultipleUpload(data
); 
 299     private void requestMultipleUpload(Intent data
) { 
 300         String
[] filePaths 
= data
.getStringArrayExtra(UploadFilesActivity
.EXTRA_CHOSEN_FILES
); 
 301         if (filePaths 
!= null
) { 
 302             String
[] remotePaths 
= new String
[filePaths
.length
]; 
 303             String remotePathBase 
= ""; 
 304             for (int j 
= mDirectories
.getCount() - 2; j 
>= 0; --j
) { 
 305                 remotePathBase 
+= OCFile
.PATH_SEPARATOR 
+ mDirectories
.getItem(j
); 
 307             if (!remotePathBase
.endsWith(OCFile
.PATH_SEPARATOR
)) 
 308                 remotePathBase 
+= OCFile
.PATH_SEPARATOR
; 
 309             for (int j 
= 0; j
< remotePaths
.length
; j
++) { 
 310                 remotePaths
[j
] = remotePathBase 
+ (new File(filePaths
[j
])).getName(); 
 313             Intent i 
= new Intent(this, FileUploader
.class); 
 314             i
.putExtra(FileUploader
.KEY_ACCOUNT
, AccountUtils
.getCurrentOwnCloudAccount(this)); 
 315             i
.putExtra(FileUploader
.KEY_LOCAL_FILE
, filePaths
); 
 316             i
.putExtra(FileUploader
.KEY_REMOTE_FILE
, remotePaths
); 
 317             i
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_MULTIPLE_FILES
); 
 321             Log
.d("FileDisplay", "User clicked on 'Update' with no selection"); 
 322             Toast t 
= Toast
.makeText(this, getString(R
.string
.filedisplay_no_file_selected
), Toast
.LENGTH_LONG
); 
 329     private void requestSimpleUpload(Intent data
) { 
 330         String filepath 
= null
; 
 332             Uri selectedImageUri 
= data
.getData(); 
 334             String filemanagerstring 
= selectedImageUri
.getPath(); 
 335             String selectedImagePath 
= getPath(selectedImageUri
); 
 337             if (selectedImagePath 
!= null
) 
 338                 filepath 
= selectedImagePath
; 
 340                 filepath 
= filemanagerstring
; 
 342         } catch (Exception e
) { 
 343             Log
.e("FileDisplay", "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e
); 
 347             if (filepath 
== null
) { 
 348                 Log
.e("FileDisplay", "Couldnt resolve path to file"); 
 349                 Toast t 
= Toast
.makeText(this, getString(R
.string
.filedisplay_unexpected_bad_get_content
), Toast
.LENGTH_LONG
); 
 355         Intent i 
= new Intent(this, FileUploader
.class); 
 356         i
.putExtra(FileUploader
.KEY_ACCOUNT
, 
 357                 AccountUtils
.getCurrentOwnCloudAccount(this)); 
 358         String remotepath 
= new String(); 
 359         for (int j 
= mDirectories
.getCount() - 2; j 
>= 0; --j
) { 
 360             remotepath 
+= OCFile
.PATH_SEPARATOR 
+ mDirectories
.getItem(j
); 
 362         if (!remotepath
.endsWith(OCFile
.PATH_SEPARATOR
)) 
 363             remotepath 
+= OCFile
.PATH_SEPARATOR
; 
 364         remotepath 
+= new File(filepath
).getName(); 
 366         i
.putExtra(FileUploader
.KEY_LOCAL_FILE
, filepath
); 
 367         i
.putExtra(FileUploader
.KEY_REMOTE_FILE
, remotepath
); 
 368         i
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_SINGLE_FILE
); 
 374     public void onBackPressed() { 
 375         if (mDirectories
.getCount() <= 1) { 
 380         mFileList
.onNavigateUp(); 
 381         mCurrentDir 
= mFileList
.getCurrentFile(); 
 384             // Resets the FileDetailsFragment on Tablets so that it always displays 
 385             FileDetailFragment fileDetails 
= (FileDetailFragment
) getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 386             if (fileDetails 
!= null 
&& !fileDetails
.isEmpty()) { 
 387                 FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 388                 transaction
.remove(fileDetails
); 
 389                 transaction
.add(R
.id
.file_details_container
, new FileDetailFragment(null
, null
)); 
 390                 transaction
.commit(); 
 394         if(mCurrentDir
.getParentId() == 0){ 
 395             ActionBar actionBar 
= getSupportActionBar();  
 396             actionBar
.setDisplayHomeAsUpEnabled(false
); 
 401     protected void onSaveInstanceState(Bundle outState
) { 
 402         // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved 
 403         Log
.d(getClass().toString(), "onSaveInstanceState() start"); 
 404         super.onSaveInstanceState(outState
); 
 405         outState
.putParcelable(FileDetailFragment
.EXTRA_FILE
, mCurrentDir
); 
 406         Log
.d(getClass().toString(), "onSaveInstanceState() end"); 
 410     protected void onResume() { 
 411         Log
.d(getClass().toString(), "onResume() start"); 
 414         if (AccountUtils
.accountsAreSetup(this)) { 
 416             if (mStorageManager 
== null
) { 
 417                 // this is necessary for handling the come back to FileDisplayActivity when the first ownCloud account is created  
 418                 initDataFromCurrentAccount(); 
 421             // Listen for sync messages 
 422             IntentFilter syncIntentFilter 
= new IntentFilter(FileSyncService
.SYNC_MESSAGE
); 
 423             mSyncBroadcastReceiver 
= new SyncBroadcastReceiver(); 
 424             registerReceiver(mSyncBroadcastReceiver
, syncIntentFilter
); 
 426             // Listen for upload messages 
 427             IntentFilter uploadIntentFilter 
= new IntentFilter(FileUploader
.UPLOAD_FINISH_MESSAGE
); 
 428             mUploadFinishReceiver 
= new UploadFinishReceiver(); 
 429             registerReceiver(mUploadFinishReceiver
, uploadIntentFilter
); 
 431             // Listen for download messages 
 432             IntentFilter downloadIntentFilter 
= new IntentFilter(FileDownloader
.DOWNLOAD_FINISH_MESSAGE
); 
 433             mDownloadFinishReceiver 
= new DownloadFinishReceiver(); 
 434             registerReceiver(mDownloadFinishReceiver
, downloadIntentFilter
); 
 436             // List current directory 
 437             mFileList
.listDirectory(mCurrentDir
);   // we should find the way to avoid the need of this 
 441             mStorageManager 
= null
;     // an invalid object will be there if all the ownCloud accounts are removed 
 442             showDialog(DIALOG_SETUP_ACCOUNT
); 
 445         Log
.d(getClass().toString(), "onResume() end"); 
 449     protected void onPause() { 
 450         Log
.d(getClass().toString(), "onPause() start"); 
 452         if (mSyncBroadcastReceiver 
!= null
) { 
 453             unregisterReceiver(mSyncBroadcastReceiver
); 
 454             mSyncBroadcastReceiver 
= null
; 
 456         if (mUploadFinishReceiver 
!= null
) { 
 457             unregisterReceiver(mUploadFinishReceiver
); 
 458             mUploadFinishReceiver 
= null
; 
 460         if (mDownloadFinishReceiver 
!= null
) { 
 461             unregisterReceiver(mDownloadFinishReceiver
); 
 462             mDownloadFinishReceiver 
= null
; 
 464         if (!AccountUtils
.accountsAreSetup(this)) { 
 465             dismissDialog(DIALOG_SETUP_ACCOUNT
); 
 468         getIntent().putExtra(FileDetailFragment
.EXTRA_FILE
, mCurrentDir
); 
 469         Log
.d(getClass().toString(), "onPause() end"); 
 473     protected Dialog 
onCreateDialog(int id
) { 
 474         Dialog dialog 
= null
; 
 475         AlertDialog
.Builder builder
; 
 477         case DIALOG_SETUP_ACCOUNT
: { 
 478             builder 
= new AlertDialog
.Builder(this); 
 479             builder
.setTitle(R
.string
.main_tit_accsetup
); 
 480             builder
.setMessage(R
.string
.main_wrn_accsetup
); 
 481             builder
.setCancelable(false
); 
 482             builder
.setPositiveButton(android
.R
.string
.ok
, new OnClickListener() { 
 483                 public void onClick(DialogInterface dialog
, int which
) { 
 484                     createFirstAccount(); 
 488             builder
.setNegativeButton(R
.string
.common_exit
, new OnClickListener() { 
 489                 public void onClick(DialogInterface dialog
, int which
) { 
 494             //builder.setNegativeButton(android.R.string.cancel, this); 
 495             dialog 
= builder
.create(); 
 498         case DIALOG_ABOUT_APP
: { 
 499             builder 
= new AlertDialog
.Builder(this); 
 500             builder
.setTitle(getString(R
.string
.about_title
)); 
 503                 pkg 
= getPackageManager().getPackageInfo(getPackageName(), 0); 
 504                 builder
.setMessage("ownCloud android client\n\nversion: " + pkg
.versionName 
); 
 505                 builder
.setIcon(android
.R
.drawable
.ic_menu_info_details
); 
 506                 dialog 
= builder
.create(); 
 507             } catch (NameNotFoundException e
) { 
 510                 Log
.e(TAG
, "Error while showing about dialog", e
); 
 514         case DIALOG_CREATE_DIR
: { 
 515             builder 
= new Builder(this); 
 516             final EditText dirNameInput 
= new EditText(getBaseContext()); 
 517             builder
.setView(dirNameInput
); 
 518             builder
.setTitle(R
.string
.uploader_info_dirname
); 
 519             int typed_color 
= getResources().getColor(R
.color
.setup_text_typed
); 
 520             dirNameInput
.setTextColor(typed_color
); 
 521             builder
.setPositiveButton(android
.R
.string
.ok
, 
 522                     new OnClickListener() { 
 523                         public void onClick(DialogInterface dialog
, int which
) { 
 524                             String directoryName 
= dirNameInput
.getText().toString(); 
 525                             if (directoryName
.trim().length() == 0) { 
 530                             // Figure out the path where the dir needs to be created 
 532                             if (mCurrentDir 
== null
) { 
 533                                 // this is just a patch; we should ensure that mCurrentDir never is null 
 534                                 if (!mStorageManager
.fileExists(OCFile
.PATH_SEPARATOR
)) { 
 535                                     OCFile file 
= new OCFile(OCFile
.PATH_SEPARATOR
); 
 536                                     mStorageManager
.saveFile(file
); 
 538                                 mCurrentDir 
= mStorageManager
.getFileByPath(OCFile
.PATH_SEPARATOR
); 
 540                             path 
= FileDisplayActivity
.this.mCurrentDir
.getRemotePath(); 
 543                             path 
+= directoryName 
+ OCFile
.PATH_SEPARATOR
; 
 544                             Thread thread 
= new Thread(new DirectoryCreator(path
,  AccountUtils
.getCurrentOwnCloudAccount(FileDisplayActivity
.this), new Handler())); 
 549                             showDialog(DIALOG_SHORT_WAIT
); 
 552             builder
.setNegativeButton(R
.string
.common_cancel
, 
 553                     new OnClickListener() { 
 554                         public void onClick(DialogInterface dialog
, int which
) { 
 558             dialog 
= builder
.create(); 
 561         case DIALOG_SHORT_WAIT
: { 
 562             ProgressDialog working_dialog 
= new ProgressDialog(this); 
 563             working_dialog
.setMessage(getResources().getString( 
 564                     R
.string
.wait_a_moment
)); 
 565             working_dialog
.setIndeterminate(true
); 
 566             working_dialog
.setCancelable(false
); 
 567             dialog 
= working_dialog
; 
 570         case DIALOG_CHOOSE_UPLOAD_SOURCE
: { 
 571             final String 
[] items 
= {   getString(R
.string
.actionbar_upload_files
),  
 572                                         getString(R
.string
.actionbar_upload_from_apps
) };  
 573             builder 
= new AlertDialog
.Builder(this); 
 574             builder
.setTitle(R
.string
.actionbar_upload
); 
 575             builder
.setItems(items
, new DialogInterface
.OnClickListener() { 
 576                 public void onClick(DialogInterface dialog
, int item
) { 
 579                             Intent action 
= new Intent(FileDisplayActivity
.this, UploadFilesActivity
.class); 
 580                             startActivityForResult(action
, ACTION_SELECT_MULTIPLE_FILES
); 
 582                             // TODO create and handle new fragment LocalFileListFragment 
 584                     } else if (item 
== 1) { 
 585                         Intent action 
= new Intent(Intent
.ACTION_GET_CONTENT
); 
 586                         action 
= action
.setType("*/*") 
 587                                 .addCategory(Intent
.CATEGORY_OPENABLE
); 
 588                         startActivityForResult( 
 589                                 Intent
.createChooser(action
, getString(R
.string
.upload_chooser_title
)), 
 590                                 ACTION_SELECT_CONTENT_FROM_APPS
); 
 594             dialog 
= builder
.create(); 
 606      * Translates a content URI of an image to a physical path 
 608      * @param uri The URI to resolve 
 609      * @return The path to the image or null if it could not be found 
 611     public String 
getPath(Uri uri
) { 
 612         String
[] projection 
= { MediaStore
.Images
.Media
.DATA 
}; 
 613         Cursor cursor 
= managedQuery(uri
, projection
, null
, null
, null
); 
 614         if (cursor 
!= null
) { 
 615             int column_index 
= cursor
 
 616                     .getColumnIndexOrThrow(MediaStore
.Images
.Media
.DATA
); 
 617             cursor
.moveToFirst(); 
 618             return cursor
.getString(column_index
); 
 624      * Pushes a directory to the drop down list 
 625      * @param directory to push 
 626      * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false. 
 628     public void pushDirname(OCFile directory
) { 
 629         if(!directory
.isDirectory()){ 
 630             throw new IllegalArgumentException("Only directories may be pushed!"); 
 632         mDirectories
.insert(directory
.getFileName(), 0); 
 633         mCurrentDir 
= directory
; 
 637      * Pops a directory name from the drop down list 
 638      * @return True, unless the stack is empty 
 640     public boolean popDirname() { 
 641         mDirectories
.remove(mDirectories
.getItem(0)); 
 642         return !mDirectories
.isEmpty(); 
 645     private class DirectoryCreator 
implements Runnable 
{ 
 646         private String mTargetPath
; 
 647         private Account mAccount
; 
 648         private Handler mHandler
;  
 650         public DirectoryCreator(String targetPath
, Account account
, Handler handler
) { 
 651             mTargetPath 
= targetPath
; 
 658             WebdavClient wdc 
= OwnCloudClientUtils
.createOwnCloudClient(mAccount
, getApplicationContext()); 
 659             boolean created 
= wdc
.createDirectory(mTargetPath
); 
 661                 mHandler
.post(new Runnable() { 
 664                         dismissDialog(DIALOG_SHORT_WAIT
); 
 666                         // Save new directory in local database 
 667                         OCFile newDir 
= new OCFile(mTargetPath
); 
 668                         newDir
.setMimetype("DIR"); 
 669                         newDir
.setParentId(mCurrentDir
.getFileId()); 
 670                         mStorageManager
.saveFile(newDir
); 
 672                         // Display the new folder right away 
 673                         mFileList
.listDirectory(mCurrentDir
); 
 678                 mHandler
.post(new Runnable() { 
 681                         dismissDialog(DIALOG_SHORT_WAIT
); 
 683                             Toast msg 
= Toast
.makeText(FileDisplayActivity
.this, R
.string
.create_dir_fail_msg
, Toast
.LENGTH_LONG
);  
 686                         } catch (NotFoundException e
) { 
 687                             Log
.e(TAG
, "Error while trying to show fail message " , e
); 
 696     // Custom array adapter to override text colors 
 697     private class CustomArrayAdapter
<T
> extends ArrayAdapter
<T
> { 
 699         public CustomArrayAdapter(FileDisplayActivity ctx
, int view
) { 
 703         public View 
getView(int position
, View convertView
, ViewGroup parent
) { 
 704             View v 
= super.getView(position
, convertView
, parent
); 
 706             ((TextView
) v
).setTextColor(getResources().getColorStateList( 
 707                     android
.R
.color
.white
)); 
 711         public View 
getDropDownView(int position
, View convertView
, 
 713             View v 
= super.getDropDownView(position
, convertView
, parent
); 
 715             ((TextView
) v
).setTextColor(getResources().getColorStateList( 
 716                     android
.R
.color
.white
)); 
 723     private class SyncBroadcastReceiver 
extends BroadcastReceiver 
{ 
 725          * {@link BroadcastReceiver} to enable syncing feedback in UI 
 728         public void onReceive(Context context
, Intent intent
) { 
 729             boolean inProgress 
= intent
.getBooleanExtra( 
 730                     FileSyncService
.IN_PROGRESS
, false
); 
 731             String accountName 
= intent
 
 732                     .getStringExtra(FileSyncService
.ACCOUNT_NAME
); 
 734             Log
.d("FileDisplay", "sync of account " + accountName
 
 735                     + " is in_progress: " + inProgress
); 
 737             if (accountName
.equals(AccountUtils
.getCurrentOwnCloudAccount(context
).name
)) {   
 739                 String synchFolderRemotePath 
= intent
.getStringExtra(FileSyncService
.SYNC_FOLDER_REMOTE_PATH
);  
 741                 boolean fillBlankRoot 
= false
; 
 742                 if (mCurrentDir 
== null
) { 
 743                     mCurrentDir 
= mStorageManager
.getFileByPath("/"); 
 744                     fillBlankRoot 
= (mCurrentDir 
!= null
); 
 747                 if ((synchFolderRemotePath 
!= null 
&& mCurrentDir 
!= null 
&& (mCurrentDir
.getRemotePath().equals(synchFolderRemotePath
))) 
 750                         mCurrentDir 
= getStorageManager().getFileByPath(synchFolderRemotePath
); 
 751                     OCFileListFragment fileListFragment 
= (OCFileListFragment
) getSupportFragmentManager() 
 752                             .findFragmentById(R
.id
.fileList
); 
 753                     if (fileListFragment 
!= null
) { 
 754                         fileListFragment
.listDirectory(mCurrentDir
);   
 758                 setSupportProgressBarIndeterminateVisibility(inProgress
); 
 765     private class UploadFinishReceiver 
extends BroadcastReceiver 
{ 
 767          * Once the file upload has finished -> update view 
 768          *  @author David A. Velasco 
 769          * {@link BroadcastReceiver} to enable upload feedback in UI 
 772         public void onReceive(Context context
, Intent intent
) { 
 773             long parentDirId 
= intent
.getLongExtra(FileUploader
.EXTRA_PARENT_DIR_ID
, -1); 
 774             OCFile parentDir 
= mStorageManager
.getFileById(parentDirId
); 
 775             String accountName 
= intent
.getStringExtra(FileUploader
.ACCOUNT_NAME
); 
 777             if (accountName
.equals(AccountUtils
.getCurrentOwnCloudAccount(context
).name
) && 
 779                     (   (mCurrentDir 
== null 
&& parentDir
.getFileName().equals("/")) || 
 780                             parentDir
.equals(mCurrentDir
) 
 783                 OCFileListFragment fileListFragment 
= (OCFileListFragment
) getSupportFragmentManager().findFragmentById(R
.id
.fileList
); 
 784                 if (fileListFragment 
!= null
) {  
 785                     fileListFragment
.listDirectory(); 
 794      * Once the file download has finished -> update view 
 796     private class DownloadFinishReceiver 
extends BroadcastReceiver 
{ 
 798         public void onReceive(Context context
, Intent intent
) { 
 799             String downloadedRemotePath 
= intent
.getStringExtra(FileDownloader
.EXTRA_REMOTE_PATH
); 
 800             String accountName 
= intent
.getStringExtra(FileDownloader
.ACCOUNT_NAME
); 
 802             if (accountName
.equals(AccountUtils
.getCurrentOwnCloudAccount(context
).name
) && 
 803                      mCurrentDir 
!= null 
&& mCurrentDir
.getFileId() == mStorageManager
.getFileByPath(downloadedRemotePath
).getParentId()) { 
 804                 OCFileListFragment fileListFragment 
= (OCFileListFragment
) getSupportFragmentManager().findFragmentById(R
.id
.fileList
); 
 805                 if (fileListFragment 
!= null
) {  
 806                     fileListFragment
.listDirectory(); 
 819     public DataStorageManager 
getStorageManager() { 
 820         return mStorageManager
; 
 828     public void onDirectoryClick(OCFile directory
) { 
 829         pushDirname(directory
); 
 830         ActionBar actionBar 
= getSupportActionBar(); 
 831         actionBar
.setDisplayHomeAsUpEnabled(true
); 
 834             // Resets the FileDetailsFragment on Tablets so that it always displays 
 835             FileDetailFragment fileDetails 
= (FileDetailFragment
) getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 836             if (fileDetails 
!= null 
&& !fileDetails
.isEmpty()) { 
 837                 FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 838                 transaction
.remove(fileDetails
); 
 839                 transaction
.add(R
.id
.file_details_container
, new FileDetailFragment(null
, null
)); 
 840                 transaction
.commit(); 
 850     public void onFileClick(OCFile file
) { 
 852         // If we are on a large device -> update fragment 
 854             // 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' 
 855             FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 856             transaction
.replace(R
.id
.file_details_container
, new FileDetailFragment(file
, AccountUtils
.getCurrentOwnCloudAccount(this)), FileDetailFragment
.FTAG
); 
 857             transaction
.setTransition(FragmentTransaction
.TRANSIT_FRAGMENT_FADE
); 
 858             transaction
.commit(); 
 860         } else {    // small or medium screen device -> new Activity 
 861             Intent showDetailsIntent 
= new Intent(this, FileDetailActivity
.class); 
 862             showDetailsIntent
.putExtra(FileDetailFragment
.EXTRA_FILE
, file
); 
 863             showDetailsIntent
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, AccountUtils
.getCurrentOwnCloudAccount(this)); 
 864             startActivity(showDetailsIntent
); 
 873     public OCFile 
getInitialDirectory() { 
 882     public void onFileStateChanged() { 
 883         OCFileListFragment fileListFragment 
= (OCFileListFragment
) getSupportFragmentManager().findFragmentById(R
.id
.fileList
); 
 884         if (fileListFragment 
!= null
) {  
 885             fileListFragment
.listDirectory(); 
 894     public FileDownloaderBinder 
getFileDownloaderBinder() { 
 895         return mDownloaderBinder
; 
 899     /** Defines callbacks for service binding, passed to bindService() */ 
 900     private ServiceConnection mConnection 
= new ServiceConnection() { 
 903         public void onServiceConnected(ComponentName className
, IBinder service
) { 
 904             mDownloaderBinder 
= (FileDownloaderBinder
) service
; 
 905             // a new chance to get the mDownloadBinder through getDownloadBinder() - THIS IS A MESS 
 906             mFileList
.listDirectory(); 
 908                 FileDetailFragment fragment 
= (FileDetailFragment
) getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 909                 if (fragment 
!= null
) 
 910                     fragment
.updateFileDetails(); 
 916         public void onServiceDisconnected(ComponentName arg0
) { 
 917             mDownloaderBinder 
= null
; 
 924      * Launch an intent to request the PIN code to the user before letting him use the app 
 926     private void requestPinCode() { 
 927         boolean pinStart 
= false
; 
 928         SharedPreferences appPrefs 
= PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()); 
 929         pinStart 
= appPrefs
.getBoolean("set_pincode", false
); 
 931             Intent i 
= new Intent(getApplicationContext(), PinCodeActivity
.class); 
 932             i
.putExtra(PinCodeActivity
.EXTRA_ACTIVITY
, "FileDisplayActivity");