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/>. 
  18 package eu
.alefzero
.owncloud
.ui
.fragment
; 
  21 import java
.io
.IOException
; 
  22 import java
.util
.ArrayList
; 
  23 import java
.util
.List
; 
  25 import org
.apache
.commons
.httpclient
.HttpException
; 
  26 import org
.apache
.commons
.httpclient
.methods
.GetMethod
; 
  27 import org
.apache
.commons
.httpclient
.methods
.PostMethod
; 
  28 import org
.apache
.commons
.httpclient
.methods
.StringRequestEntity
; 
  29 import org
.apache
.commons
.httpclient
.params
.HttpConnectionManagerParams
; 
  30 import org
.apache
.http
.HttpStatus
; 
  31 import org
.apache
.http
.NameValuePair
; 
  32 import org
.apache
.http
.client
.utils
.URLEncodedUtils
; 
  33 import org
.apache
.http
.message
.BasicNameValuePair
; 
  34 import org
.apache
.http
.protocol
.HTTP
; 
  35 import org
.apache
.jackrabbit
.webdav
.client
.methods
.DavMethodBase
; 
  36 import org
.apache
.jackrabbit
.webdav
.client
.methods
.DeleteMethod
; 
  37 import org
.apache
.jackrabbit
.webdav
.client
.methods
.PropFindMethod
; 
  38 import org
.json
.JSONException
; 
  39 import org
.json
.JSONObject
; 
  41 import android
.accounts
.Account
; 
  42 import android
.accounts
.AccountManager
; 
  43 import android
.content
.ActivityNotFoundException
; 
  44 import android
.content
.BroadcastReceiver
; 
  45 import android
.content
.Context
; 
  46 import android
.content
.Intent
; 
  47 import android
.content
.IntentFilter
; 
  48 import android
.content
.res
.Resources
.NotFoundException
; 
  49 import android
.graphics
.Bitmap
; 
  50 import android
.graphics
.BitmapFactory
; 
  51 import android
.graphics
.BitmapFactory
.Options
; 
  52 import android
.graphics
.Point
; 
  53 import android
.net
.Uri
; 
  54 import android
.os
.AsyncTask
; 
  55 import android
.os
.Bundle
; 
  56 import android
.os
.Handler
; 
  57 import android
.support
.v4
.app
.FragmentTransaction
; 
  58 import android
.util
.Log
; 
  59 import android
.view
.Display
; 
  60 import android
.view
.LayoutInflater
; 
  61 import android
.view
.View
; 
  62 import android
.view
.View
.OnClickListener
; 
  63 import android
.view
.ViewGroup
; 
  64 import android
.view
.WindowManager
.LayoutParams
; 
  65 import android
.webkit
.MimeTypeMap
; 
  66 import android
.widget
.Button
; 
  67 import android
.widget
.CheckBox
; 
  68 import android
.widget
.ImageView
; 
  69 import android
.widget
.TextView
; 
  70 import android
.widget
.Toast
; 
  72 import com
.actionbarsherlock
.app
.SherlockDialogFragment
; 
  73 import com
.actionbarsherlock
.app
.SherlockFragment
; 
  75 import eu
.alefzero
.owncloud
.AccountUtils
; 
  76 import eu
.alefzero
.owncloud
.DisplayUtils
; 
  77 import eu
.alefzero
.owncloud
.R
; 
  78 import eu
.alefzero
.owncloud
.authenticator
.AccountAuthenticator
; 
  79 import eu
.alefzero
.owncloud
.datamodel
.FileDataStorageManager
; 
  80 import eu
.alefzero
.owncloud
.datamodel
.OCFile
; 
  81 import eu
.alefzero
.owncloud
.files
.services
.FileDownloader
; 
  82 import eu
.alefzero
.owncloud
.files
.services
.FileUploader
; 
  83 import eu
.alefzero
.owncloud
.ui
.activity
.FileDisplayActivity
; 
  84 import eu
.alefzero
.owncloud
.utils
.OwnCloudVersion
; 
  85 import eu
.alefzero
.webdav
.WebdavClient
; 
  86 import eu
.alefzero
.webdav
.WebdavUtils
; 
  89  * This Fragment is used to display the details about a file. 
  91  * @author Bartek Przybylski 
  94 public class FileDetailFragment 
extends SherlockFragment 
implements 
  95         OnClickListener
, ConfirmationDialogFragment
.ConfirmationDialogFragmentListener 
{ 
  97     public static final String EXTRA_FILE 
= "FILE"; 
  98     public static final String EXTRA_ACCOUNT 
= "ACCOUNT"; 
 102     private OCFile mFile
; 
 103     private Account mAccount
; 
 104     private ImageView mPreview
; 
 106     private DownloadFinishReceiver mDownloadFinishReceiver
; 
 108     private static final String TAG 
= "FileDetailFragment"; 
 109     public static final String FTAG 
= "FileDetails";  
 110     public static final String FTAG_CONFIRMATION 
= "REMOVE_CONFIRMATION_FRAGMENT"; 
 114      * Creates an empty details fragment. 
 116      * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.  
 118     public FileDetailFragment() { 
 121         mLayout 
= R
.layout
.file_details_empty
; 
 126      * Creates a details fragment. 
 128      * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). 
 130      * @param fileToDetail      An {@link OCFile} to show in the fragment 
 131      * @param ocAccount         An ownCloud account; needed to start downloads 
 133     public FileDetailFragment(OCFile fileToDetail
, Account ocAccount
){ 
 134         mFile 
= fileToDetail
; 
 135         mAccount 
= ocAccount
; 
 136         mLayout 
= R
.layout
.file_details_empty
; 
 138         if(fileToDetail 
!= null 
&& ocAccount 
!= null
) { 
 139             mLayout 
= R
.layout
.file_details_fragment
; 
 145     public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, 
 146             Bundle savedInstanceState
) { 
 147         super.onCreateView(inflater
, container
, savedInstanceState
); 
 149         if (savedInstanceState 
!= null
) { 
 150             mFile 
= savedInstanceState
.getParcelable(FileDetailFragment
.EXTRA_FILE
); 
 151             mAccount 
= savedInstanceState
.getParcelable(FileDetailFragment
.EXTRA_ACCOUNT
); 
 155         view 
= inflater
.inflate(mLayout
, container
, false
); 
 158         if (mLayout 
== R
.layout
.file_details_fragment
) { 
 159             mView
.findViewById(R
.id
.fdKeepInSync
).setOnClickListener(this); 
 160             mView
.findViewById(R
.id
.fdRenameBtn
).setOnClickListener(this); 
 161             mView
.findViewById(R
.id
.fdDownloadBtn
).setOnClickListener(this); 
 162             mView
.findViewById(R
.id
.fdOpenBtn
).setOnClickListener(this); 
 163             mView
.findViewById(R
.id
.fdRemoveBtn
).setOnClickListener(this); 
 164             //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this); 
 165             mPreview 
= (ImageView
)mView
.findViewById(R
.id
.fdPreview
); 
 174     public void onSaveInstanceState(Bundle outState
) { 
 175         Log
.i(getClass().toString(), "onSaveInstanceState() start"); 
 176         super.onSaveInstanceState(outState
); 
 177         outState
.putParcelable(FileDetailFragment
.EXTRA_FILE
, mFile
); 
 178         outState
.putParcelable(FileDetailFragment
.EXTRA_ACCOUNT
, mAccount
); 
 179         Log
.i(getClass().toString(), "onSaveInstanceState() end"); 
 184     public void onResume() { 
 186         mDownloadFinishReceiver 
= new DownloadFinishReceiver(); 
 187         IntentFilter filter 
= new IntentFilter( 
 188                 FileDownloader
.DOWNLOAD_FINISH_MESSAGE
); 
 189         getActivity().registerReceiver(mDownloadFinishReceiver
, filter
); 
 190         mPreview 
= (ImageView
)mView
.findViewById(R
.id
.fdPreview
); 
 194     public void onPause() { 
 196         getActivity().unregisterReceiver(mDownloadFinishReceiver
); 
 197         mDownloadFinishReceiver 
= null
; 
 198         if (mPreview 
!= null
) { 
 204     public View 
getView() { 
 205         return super.getView() == null ? mView 
: super.getView(); 
 211     public void onClick(View v
) { 
 213             case R
.id
.fdDownloadBtn
: { 
 214                 Intent i 
= new Intent(getActivity(), FileDownloader
.class); 
 215                 i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, mAccount
); 
 216                 i
.putExtra(FileDownloader
.EXTRA_REMOTE_PATH
, mFile
.getRemotePath()); 
 217                 i
.putExtra(FileDownloader
.EXTRA_FILE_PATH
, mFile
.getRemotePath()); 
 218                 i
.putExtra(FileDownloader
.EXTRA_FILE_SIZE
, mFile
.getFileLength()); 
 221                 Toast
.makeText(getActivity(), "Downloading", Toast
.LENGTH_LONG
).show(); 
 222                 setButtonsForDownloading(); 
 224                 getActivity().startService(i
); 
 227             case R
.id
.fdKeepInSync
: { 
 228                 CheckBox cb 
= (CheckBox
) getView().findViewById(R
.id
.fdKeepInSync
); 
 229                 mFile
.setKeepInSync(cb
.isChecked()); 
 230                 FileDataStorageManager fdsm 
= new FileDataStorageManager(mAccount
, getActivity().getApplicationContext().getContentResolver()); 
 231                 fdsm
.saveFile(mFile
); 
 232                 if (mFile
.keepInSync()) { 
 233                     onClick(getView().findViewById(R
.id
.fdDownloadBtn
)); 
 237             case R
.id
.fdRenameBtn
: { 
 238                 EditNameFragment dialog 
= EditNameFragment
.newInstance(mFile
.getFileName()); 
 239                 dialog
.show(getFragmentManager(), "nameeditdialog"); 
 240                 dialog
.setOnDismissListener(this); 
 243             case R
.id
.fdRemoveBtn
: { 
 244                 ConfirmationDialogFragment confDialog 
= ConfirmationDialogFragment
.newInstance(R
.string
.confirmation_remove_alert
, new String
[]{mFile
.getFileName()}); 
 245                 confDialog
.setOnConfirmationListener(this); 
 246                 confDialog
.show(getFragmentManager(), FTAG_CONFIRMATION
); 
 249             case R
.id
.fdOpenBtn
: { 
 250                 String storagePath 
= mFile
.getStoragePath(); 
 251                 String encodedStoragePath 
= WebdavUtils
.encodePath(storagePath
); 
 253                     Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 254                     i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mFile
.getMimetype()); 
 255                     i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 258                 } catch (Throwable t
) { 
 259                     Log
.e(TAG
, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile
.getMimetype()); 
 260                     boolean toastIt 
= true
;  
 261                     String mimeType 
= ""; 
 263                         Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 264                         mimeType 
= MimeTypeMap
.getSingleton().getMimeTypeFromExtension(storagePath
.substring(storagePath
.lastIndexOf('.') + 1)); 
 265                         if (mimeType 
!= null 
&& !mimeType
.equals(mFile
.getMimetype())) { 
 266                             i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mimeType
); 
 267                             i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 272                     } catch (IndexOutOfBoundsException e
) { 
 273                         Log
.e(TAG
, "Trying to find out MIME type of a file without extension: " + storagePath
); 
 275                     } catch (ActivityNotFoundException e
) { 
 276                         Log
.e(TAG
, "No activity found to handle: " + storagePath 
+ " with MIME type " + mimeType 
+ " obtained from extension"); 
 278                     } catch (Throwable th
) { 
 279                         Log
.e(TAG
, "Unexpected problem when opening: " + storagePath
, th
); 
 283                             Toast
.makeText(getActivity(), "There is no application to handle file " + mFile
.getFileName(), Toast
.LENGTH_SHORT
).show(); 
 291                 Log
.e(TAG
, "Incorrect view clicked!"); 
 294         /* else if (v.getId() == R.id.fdShareBtn) { 
 295             Thread t = new Thread(new ShareRunnable(mFile.getRemotePath())); 
 302     public void onConfirmation(boolean confirmation
, String callerTag
) { 
 303         if (confirmation 
&& callerTag
.equals(FTAG_CONFIRMATION
)) { 
 304             Log
.e("ASD","onConfirmation"); 
 305             FileDataStorageManager fdsm 
= new FileDataStorageManager(mAccount
, getActivity().getContentResolver()); 
 306             if (fdsm
.getFileById(mFile
.getFileId()) != null
) { 
 307                 new Thread(new RemoveRunnable(mFile
, mAccount
, new Handler())).start(); 
 309         } else if (!confirmation
) Log
.d(TAG
, "REMOVAL CANCELED"); 
 314      * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. 
 316      * @return  True when the fragment was created with the empty layout. 
 318     public boolean isEmpty() { 
 319         return mLayout 
== R
.layout
.file_details_empty
; 
 324      * Can be used to get the file that is currently being displayed. 
 325      * @return The file on the screen. 
 327     public OCFile 
getDisplayedFile(){ 
 332      * Use this method to signal this Activity that it shall update its view. 
 334      * @param file : An {@link OCFile} 
 336     public void updateFileDetails(OCFile file
, Account ocAccount
) { 
 338         mAccount 
= ocAccount
; 
 344      * Updates the view with all relevant details about that file. 
 346     public void updateFileDetails() { 
 348         if (mFile 
!= null 
&& mAccount 
!= null 
&& mLayout 
== R
.layout
.file_details_fragment
) { 
 351             setFilename(mFile
.getFileName()); 
 352             setFiletype(DisplayUtils
.convertMIMEtoPrettyPrint(mFile
 
 354             setFilesize(mFile
.getFileLength()); 
 355             if(ocVersionSupportsTimeCreated()){ 
 356                 setTimeCreated(mFile
.getCreationTimestamp()); 
 359             setTimeModified(mFile
.getModificationTimestamp()); 
 361             CheckBox cb 
= (CheckBox
)getView().findViewById(R
.id
.fdKeepInSync
); 
 362             cb
.setChecked(mFile
.keepInSync()); 
 364             // configure UI for depending upon local state of the file 
 365             if (FileDownloader
.isDownloading(mAccount
, mFile
.getRemotePath())) { 
 366                 setButtonsForDownloading(); 
 368             } else if (mFile
.isDown()) { 
 370                 if (mFile
.getMimetype().startsWith("image/")) { 
 371                     BitmapLoader bl 
= new BitmapLoader(); 
 372                     bl
.execute(new String
[]{mFile
.getStoragePath()}); 
 377                 // Change download button to open button 
 378                 /*downloadButton.setText(R.string.filedetails_open); 
 379                 downloadButton.setOnClickListener(new OnClickListener() { 
 381                     public void onClick(View v) { 
 382                         String storagePath = mFile.getStoragePath(); 
 383                         String encodedStoragePath = WebdavUtils.encodePath(storagePath); 
 385                             Intent i = new Intent(Intent.ACTION_VIEW); 
 386                             i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype()); 
 387                             i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 
 390                         } catch (Throwable t) { 
 391                             Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype()); 
 392                             boolean toastIt = true;  
 393                             String mimeType = ""; 
 395                                 Intent i = new Intent(Intent.ACTION_VIEW); 
 396                                 mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1)); 
 397                                 if (mimeType != null && !mimeType.equals(mFile.getMimetype())) { 
 398                                     i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType); 
 399                                     i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 
 404                             } catch (IndexOutOfBoundsException e) { 
 405                                 Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); 
 407                             } catch (ActivityNotFoundException e) { 
 408                                 Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); 
 410                             } catch (Throwable th) { 
 411                                 Log.e(TAG, "Unexpected problem when opening: " + storagePath, th); 
 415                                     Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show(); 
 423                 setButtonsForRemote(); 
 430      * Updates the filename in view 
 431      * @param filename to set 
 433     private void setFilename(String filename
) { 
 434         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdFilename
); 
 436             tv
.setText(filename
); 
 440      * Updates the MIME type in view 
 441      * @param mimetype to set 
 443     private void setFiletype(String mimetype
) { 
 444         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdType
); 
 446             tv
.setText(mimetype
); 
 450      * Updates the file size in view 
 451      * @param filesize in bytes to set 
 453     private void setFilesize(long filesize
) { 
 454         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdSize
); 
 456             tv
.setText(DisplayUtils
.bytesToHumanReadable(filesize
)); 
 460      * Updates the time that the file was created in view 
 461      * @param milliseconds Unix time to set 
 463     private void setTimeCreated(long milliseconds
){ 
 464         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdCreated
); 
 465         TextView tvLabel 
= (TextView
) getView().findViewById(R
.id
.fdCreatedLabel
); 
 467             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 468             tv
.setVisibility(View
.VISIBLE
); 
 469             tvLabel
.setVisibility(View
.VISIBLE
); 
 474      * Updates the time that the file was last modified 
 475      * @param milliseconds Unix time to set 
 477     private void setTimeModified(long milliseconds
){ 
 478         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdModified
); 
 480             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 485      * Enables or disables buttons for a file being downloaded 
 487     private void setButtonsForDownloading() { 
 489             Button downloadButton 
= (Button
) getView().findViewById(R
.id
.fdDownloadBtn
); 
 490             downloadButton
.setText(R
.string
.filedetails_download_in_progress
); 
 491             downloadButton
.setEnabled(false
);   // TODO replace it with a 'cancel download' button 
 493             // let's protect the user from himself ;) 
 494             ((Button
) getView().findViewById(R
.id
.fdOpenBtn
)).setEnabled(false
); 
 495             ((Button
) getView().findViewById(R
.id
.fdRenameBtn
)).setEnabled(false
); 
 496             ((Button
) getView().findViewById(R
.id
.fdRemoveBtn
)).setEnabled(false
); 
 501      * Enables or disables buttons for a file locally available  
 503     private void setButtonsForDown() { 
 505             Button downloadButton 
= (Button
) getView().findViewById(R
.id
.fdDownloadBtn
); 
 506             downloadButton
.setText(R
.string
.filedetails_redownload
); 
 507             downloadButton
.setEnabled(true
); 
 509             ((Button
) getView().findViewById(R
.id
.fdOpenBtn
)).setEnabled(true
); 
 510             ((Button
) getView().findViewById(R
.id
.fdRenameBtn
)).setEnabled(true
); 
 511             ((Button
) getView().findViewById(R
.id
.fdRemoveBtn
)).setEnabled(true
); 
 516      * Enables or disables buttons for a file not locally available  
 518     private void setButtonsForRemote() { 
 520             Button downloadButton 
= (Button
) getView().findViewById(R
.id
.fdDownloadBtn
); 
 521             downloadButton
.setText(R
.string
.filedetails_download
); 
 522             downloadButton
.setEnabled(true
); 
 524             ((Button
) getView().findViewById(R
.id
.fdOpenBtn
)).setEnabled(false
); 
 525             ((Button
) getView().findViewById(R
.id
.fdRenameBtn
)).setEnabled(true
); 
 526             ((Button
) getView().findViewById(R
.id
.fdRemoveBtn
)).setEnabled(true
); 
 532      * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return 
 533      * the time that the file was created. There is a chance that this will 
 534      * be fixed in future versions. Use this method to check if this version of 
 535      * ownCloud has this fix. 
 536      * @return True, if ownCloud the ownCloud version is supporting creation time 
 538     private boolean ocVersionSupportsTimeCreated(){ 
 539         /*if(mAccount != null){ 
 540             AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE); 
 541             OwnCloudVersion ocVersion = new OwnCloudVersion(accManager 
 542                     .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION)); 
 543             if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) { 
 551      * Once the file download has finished -> update view 
 552      * @author Bartek Przybylski 
 554     private class DownloadFinishReceiver 
extends BroadcastReceiver 
{ 
 556         public void onReceive(Context context
, Intent intent
) { 
 557             String accountName 
= intent
.getStringExtra(FileDownloader
.ACCOUNT_NAME
); 
 559             if (accountName
.equals(mAccount
.name
) && mFile 
!= null
) { 
 560                 boolean downloadWasFine 
= intent
.getBooleanExtra(FileDownloader
.EXTRA_DOWNLOAD_RESULT
, false
); 
 561                 String downloadedRemotePath 
= intent
.getStringExtra(FileDownloader
.EXTRA_REMOTE_PATH
); 
 562                 if (mFile
.getRemotePath().equals(downloadedRemotePath
)) { 
 563                     if (downloadWasFine
) { 
 564                         mFile
.setStoragePath(intent
.getStringExtra(FileDownloader
.EXTRA_FILE_PATH
)); 
 566                     updateFileDetails();    // it updates the buttons; must be called although !downloadWasFine 
 572     // this is a temporary class for sharing purposes, it need to be replaced in transfer service 
 573     private class ShareRunnable 
implements Runnable 
{ 
 574         private String mPath
; 
 576         public ShareRunnable(String path
) { 
 581             AccountManager am 
= AccountManager
.get(getActivity()); 
 582             Account account 
= AccountUtils
.getCurrentOwnCloudAccount(getActivity()); 
 583             OwnCloudVersion ocv 
= new OwnCloudVersion(am
.getUserData(account
, AccountAuthenticator
.KEY_OC_VERSION
)); 
 584             String url 
= am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + AccountUtils
.getWebdavPath(ocv
); 
 586             Log
.d("share", "sharing for version " + ocv
.toString()); 
 588             if (ocv
.compareTo(new OwnCloudVersion(0x040000)) >= 0) { 
 589                 String APPS_PATH 
= "/apps/files_sharing/"; 
 590                 String SHARE_PATH 
= "ajax/share.php"; 
 592                 String SHARED_PATH 
= "/apps/files_sharing/get.php?token="; 
 594                 final String WEBDAV_SCRIPT 
= "webdav.php"; 
 595                 final String WEBDAV_FILES_LOCATION 
= "/files/"; 
 597                 WebdavClient wc 
= new WebdavClient(); 
 598                 HttpConnectionManagerParams params 
= new HttpConnectionManagerParams(); 
 599                 params
.setMaxConnectionsPerHost(wc
.getHostConfiguration(), 5); 
 601                 //wc.getParams().setParameter("http.protocol.single-cookie-header", true); 
 602                 //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); 
 604                 PostMethod post 
= new PostMethod(am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + APPS_PATH 
+ SHARE_PATH
); 
 606                 post
.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" ); 
 607                 post
.addRequestHeader("Referer", am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
)); 
 608                 List
<NameValuePair
> formparams 
= new ArrayList
<NameValuePair
>(); 
 609                 Log
.d("share", mPath
+""); 
 610                 formparams
.add(new BasicNameValuePair("sources",mPath
)); 
 611                 formparams
.add(new BasicNameValuePair("uid_shared_with", "public")); 
 612                 formparams
.add(new BasicNameValuePair("permissions", "0")); 
 613                 post
.setRequestEntity(new StringRequestEntity(URLEncodedUtils
.format(formparams
, HTTP
.UTF_8
))); 
 617                     PropFindMethod find 
= new PropFindMethod(url
+"/"); 
 618                     find
.addRequestHeader("Referer", am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
)); 
 619                     Log
.d("sharer", ""+ url
+"/"); 
 620                     wc
.setCredentials(account
.name
.substring(0, account
.name
.lastIndexOf('@')), am
.getPassword(account
)); 
 622                     for (org
.apache
.commons
.httpclient
.Header a 
: find
.getRequestHeaders()) { 
 623                         Log
.d("sharer-h", a
.getName() + ":"+a
.getValue()); 
 626                     int status2 
= wc
.executeMethod(find
); 
 628                     Log
.d("sharer", "propstatus "+status2
); 
 630                     GetMethod get 
= new GetMethod(am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + "/"); 
 631                     get
.addRequestHeader("Referer", am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
)); 
 633                     status2 
= wc
.executeMethod(get
); 
 635                     Log
.d("sharer", "getstatus "+status2
); 
 636                     Log
.d("sharer", "" + get
.getResponseBodyAsString()); 
 638                     for (org
.apache
.commons
.httpclient
.Header a 
: get
.getResponseHeaders()) { 
 639                         Log
.d("sharer", a
.getName() + ":"+a
.getValue()); 
 642                     status 
= wc
.executeMethod(post
); 
 643                     for (org
.apache
.commons
.httpclient
.Header a 
: post
.getRequestHeaders()) { 
 644                         Log
.d("sharer-h", a
.getName() + ":"+a
.getValue()); 
 646                     for (org
.apache
.commons
.httpclient
.Header a 
: post
.getResponseHeaders()) { 
 647                         Log
.d("sharer", a
.getName() + ":"+a
.getValue()); 
 649                     String resp 
= post
.getResponseBodyAsString(); 
 650                     Log
.d("share", ""+post
.getURI().toString()); 
 651                     Log
.d("share", "returned status " + status
); 
 652                     Log
.d("share", " " +resp
); 
 654                     if(status 
!= HttpStatus
.SC_OK 
||resp 
== null 
|| resp
.equals("") || resp
.startsWith("false")) { 
 658                     JSONObject jsonObject 
= new JSONObject (resp
); 
 659                     String jsonStatus 
= jsonObject
.getString("status"); 
 660                     if(!jsonStatus
.equals("success")) throw new Exception("Error while sharing file status != success"); 
 662                     String token 
= jsonObject
.getString("data"); 
 663                     String uri 
= am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + SHARED_PATH 
+ token
;  
 664                     Log
.d("Actions:shareFile ok", "url: " + uri
);    
 666                 } catch (HttpException e
) { 
 667                     // TODO Auto-generated catch block 
 669                 } catch (IOException e
) { 
 670                     // TODO Auto-generated catch block 
 672                 } catch (JSONException e
) { 
 673                     // TODO Auto-generated catch block 
 675                 } catch (Exception e
) { 
 676                     // TODO Auto-generated catch block 
 680             } else if (ocv
.compareTo(new OwnCloudVersion(0x030000)) >= 0) { 
 686     public void onDismiss(EditNameFragment dialog
) { 
 687         if (dialog 
instanceof EditNameFragment
) { 
 688             if (((EditNameFragment
)dialog
).getResult()) { 
 689                 String newFilename 
= ((EditNameFragment
)dialog
).getNewFilename(); 
 690                 Log
.d(TAG
, "name edit dialog dismissed with new name " + newFilename
); 
 691                 if (!newFilename
.equals(mFile
.getFileName())) { 
 692                     FileDataStorageManager fdsm 
= new FileDataStorageManager(mAccount
, getActivity().getContentResolver()); 
 693                     if (fdsm
.getFileById(mFile
.getFileId()) != null
) { 
 694                         OCFile newFile 
= new OCFile(fdsm
.getFileById(mFile
.getParentId()).getRemotePath() + newFilename
); 
 695                         newFile
.setCreationTimestamp(mFile
.getCreationTimestamp()); 
 696                         newFile
.setFileId(mFile
.getFileId()); 
 697                         newFile
.setFileLength(mFile
.getFileLength()); 
 698                         newFile
.setKeepInSync(mFile
.keepInSync()); 
 699                         newFile
.setLastSyncDate(mFile
.getLastSyncDate()); 
 700                         newFile
.setMimetype(mFile
.getMimetype()); 
 701                         newFile
.setModificationTimestamp(mFile
.getModificationTimestamp()); 
 702                         newFile
.setParentId(mFile
.getParentId()); 
 703                         if (mFile
.isDown()) { 
 704                             File f 
= new File(mFile
.getStoragePath()); 
 705                             Log
.e(TAG
, f
.getAbsolutePath()); 
 706                             f
.renameTo(new File(f
.getParent() + File
.separator 
+ newFilename
)); // TODO check if fails 
 707                             Log
.e(TAG
, f
.getParent() + File
.separator 
+ newFilename
); 
 708                             newFile
.setStoragePath(f
.getParent() + File
.separator 
+ newFilename
); 
 711                         new Thread(new RenameRunnable(mFile
, newFile
, mAccount
, new Handler())).start(); 
 717             Log
.e(TAG
, "Unknown dialog instance passed to onDismissDalog: " + dialog
.getClass().getCanonicalName()); 
 722     private class RenameRunnable 
implements Runnable 
{ 
 728         public RenameRunnable(OCFile oldFile
, OCFile newFile
, Account account
, Handler handler
) { 
 736             WebdavClient wc 
= new WebdavClient(mAccount
, getSherlockActivity().getApplicationContext()); 
 737             AccountManager am 
= AccountManager
.get(getSherlockActivity()); 
 738             String baseUrl 
= am
.getUserData(mAccount
, AccountAuthenticator
.KEY_OC_BASE_URL
); 
 739             OwnCloudVersion ocv 
= new OwnCloudVersion(am
.getUserData(mAccount
, AccountAuthenticator
.KEY_OC_VERSION
)); 
 740             String webdav_path 
= AccountUtils
.getWebdavPath(ocv
); 
 741             Log
.d("ASD", ""+baseUrl 
+ webdav_path 
+ WebdavUtils
.encodePath(mOld
.getRemotePath())); 
 743             Log
.e("ASD", Uri
.parse(baseUrl
).getPath() == null ? 
"" : Uri
.parse(baseUrl
).getPath() + webdav_path 
+ WebdavUtils
.encodePath(mNew
.getRemotePath())); 
 744             LocalMoveMethod move 
= new LocalMoveMethod(baseUrl 
+ webdav_path 
+ WebdavUtils
.encodePath(mOld
.getRemotePath()), 
 745                                              Uri
.parse(baseUrl
).getPath() == null ? 
"" : Uri
.parse(baseUrl
).getPath() + webdav_path 
+ WebdavUtils
.encodePath(mNew
.getRemotePath())); 
 748                 int status 
= wc
.executeMethod(move
); 
 749                 if (move
.succeeded()) { 
 750                     FileDataStorageManager fdsm 
= new FileDataStorageManager(mAccount
, getActivity().getContentResolver()); 
 751                     fdsm
.removeFile(mOld
); 
 754                     mHandler
.post(new Runnable() { 
 756                         public void run() { updateFileDetails(mFile
, mAccount
); } 
 759                 Log
.e("ASD", ""+move
.getQueryString()); 
 760                 Log
.d("move", "returned status " + status
); 
 761             } catch (HttpException e
) { 
 762                 // TODO Auto-generated catch block 
 764             } catch (IOException e
) { 
 765                 // TODO Auto-generated catch block 
 769         private class LocalMoveMethod 
extends DavMethodBase 
{ 
 771             public LocalMoveMethod(String uri
, String dest
) { 
 773                 addRequestHeader(new org
.apache
.commons
.httpclient
.Header("Destination", dest
)); 
 777             public String 
getName() { 
 782             protected boolean isSuccess(int status
) { 
 783                 return status 
== 201 || status 
== 204; 
 789     private static class EditNameFragment 
extends SherlockDialogFragment 
implements OnClickListener 
{ 
 791         private String mNewFilename
; 
 792         private boolean mResult
; 
 793         private FileDetailFragment mListener
; 
 795         static public EditNameFragment 
newInstance(String filename
) { 
 796             EditNameFragment f 
= new EditNameFragment(); 
 797             Bundle args 
= new Bundle(); 
 798             args
.putString("filename", filename
); 
 799             f
.setArguments(args
); 
 804         public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, Bundle savedInstanceState
) { 
 805             View v 
= inflater
.inflate(R
.layout
.edit_box_dialog
, container
, false
); 
 807             String currentName 
= getArguments().getString("filename"); 
 808             if (currentName 
== null
) 
 811             ((Button
)v
.findViewById(R
.id
.cancel
)).setOnClickListener(this); 
 812             ((Button
)v
.findViewById(R
.id
.ok
)).setOnClickListener(this); 
 813             ((TextView
)v
.findViewById(R
.id
.user_input
)).setText(currentName
); 
 814             ((TextView
)v
.findViewById(R
.id
.user_input
)).requestFocus(); 
 815             getDialog().getWindow().setSoftInputMode(LayoutParams
.SOFT_INPUT_STATE_VISIBLE
); 
 822         public void onClick(View view
) { 
 823             switch (view
.getId()) { 
 825                     mNewFilename 
= ((TextView
)getView().findViewById(R
.id
.user_input
)).getText().toString(); 
 828                 case R
.id
.cancel
: { // fallthought 
 830                     mListener
.onDismiss(this); 
 835         void setOnDismissListener(FileDetailFragment listener
) { 
 836             mListener 
= listener
; 
 839         public String 
getNewFilename() { 
 843         // true if user click ok 
 844         public boolean getResult() { 
 850     private class RemoveRunnable 
implements Runnable 
{ 
 852         /** Arbitrary timeout for deletion */ 
 853         public final static int DELETION_TIMEOUT 
= 5000; 
 856         OCFile mFileToRemove
; 
 859         public RemoveRunnable(OCFile fileToRemove
, Account account
, Handler handler
) { 
 860             mFileToRemove 
= fileToRemove
; 
 866             WebdavClient wc 
= new WebdavClient(mAccount
, getSherlockActivity().getApplicationContext()); 
 867             AccountManager am 
= AccountManager
.get(getSherlockActivity()); 
 868             String baseUrl 
= am
.getUserData(mAccount
, AccountAuthenticator
.KEY_OC_BASE_URL
); 
 869             OwnCloudVersion ocv 
= new OwnCloudVersion(am
.getUserData(mAccount
, AccountAuthenticator
.KEY_OC_VERSION
)); 
 870             String webdav_path 
= AccountUtils
.getWebdavPath(ocv
); 
 871             Log
.d("ASD", ""+baseUrl 
+ webdav_path 
+ WebdavUtils
.encodePath(mFileToRemove
.getRemotePath())); 
 873             DeleteMethod delete 
= new DeleteMethod(baseUrl 
+ webdav_path 
+ WebdavUtils
.encodePath(mFileToRemove
.getRemotePath())); 
 875             boolean success 
= false
; 
 877                 int status 
= wc
.executeMethod(delete
, DELETION_TIMEOUT
); 
 878                 if (delete
.succeeded()) { 
 879                     FileDataStorageManager fdsm 
= new FileDataStorageManager(mAccount
, getActivity().getContentResolver()); 
 880                     fdsm
.removeFile(mFileToRemove
); 
 881                     mHandler
.post(new Runnable() { 
 885                                 Toast msg 
= Toast
.makeText(getActivity().getApplicationContext(), R
.string
.remove_success_msg
, Toast
.LENGTH_LONG
); 
 887                                 if (getActivity() instanceof FileDisplayActivity
) { 
 889                                     FragmentTransaction transaction 
= getActivity().getSupportFragmentManager().beginTransaction(); 
 890                                     transaction
.replace(R
.id
.file_details_container
, new FileDetailFragment(null
, null
)); // empty FileDetailFragment 
 891                                     transaction
.commit(); 
 894                                     getActivity().finish(); 
 897                             } catch (NotFoundException e
) { 
 904                 Log
.e("ASD", ""+ delete
.getQueryString()); 
 905                 Log
.d("delete", "returned status " + status
); 
 907             } catch (HttpException e
) { 
 910             } catch (IOException e
) { 
 915                     mHandler
.post(new Runnable() { 
 919                                 Toast msg 
= Toast
.makeText(getActivity(), R
.string
.remove_fail_msg
, Toast
.LENGTH_LONG
);  
 922                             } catch (NotFoundException e
) { 
 933     class BitmapLoader 
extends AsyncTask
<String
, Void
, Bitmap
> { 
 935         protected Bitmap 
doInBackground(String
... params
) { 
 936             Bitmap result 
= null
; 
 937             if (params
.length 
!= 1) return result
; 
 938             String storagePath 
= params
[0]; 
 941                 BitmapFactory
.Options options 
= new Options(); 
 942                 options
.inScaled 
= true
; 
 943                 options
.inPurgeable 
= true
; 
 944                 options
.inJustDecodeBounds 
= true
; 
 945                 if (android
.os
.Build
.VERSION
.SDK_INT 
>= android
.os
.Build
.VERSION_CODES
.GINGERBREAD_MR1
) { 
 946                     options
.inPreferQualityOverSpeed 
= false
; 
 948                 if (android
.os
.Build
.VERSION
.SDK_INT 
>= android
.os
.Build
.VERSION_CODES
.HONEYCOMB
) { 
 949                     options
.inMutable 
= false
; 
 952                 result 
= BitmapFactory
.decodeFile(storagePath
, options
); 
 953                 options
.inJustDecodeBounds 
= false
; 
 955                 int width 
= options
.outWidth
; 
 956                 int height 
= options
.outHeight
; 
 958                 boolean recycle 
= false
; 
 959                 if (width 
>= 2048 || height 
>= 2048) { 
 960                     scale 
= (int) Math
.ceil((Math
.ceil(Math
.max(height
, width
) / 2048.))); 
 961                     options
.inSampleSize 
= scale
; 
 963                 Display display 
= getActivity().getWindowManager().getDefaultDisplay(); 
 964                 Point size 
= new Point(); 
 966                 if (android
.os
.Build
.VERSION
.SDK_INT 
>= android
.os
.Build
.VERSION_CODES
.HONEYCOMB_MR2
) { 
 967                     display
.getSize(size
); 
 968                     screenwidth 
= size
.x
; 
 970                     screenwidth 
= display
.getWidth(); 
 973                 Log
.e("ASD", "W " + width 
+ " SW " + screenwidth
); 
 975                 if (width 
> screenwidth
) { 
 976                     scale 
= (int) Math
.ceil((float)width 
/ screenwidth
); 
 977                     options
.inSampleSize 
= scale
; 
 980                 result 
= BitmapFactory
.decodeFile(storagePath
, options
); 
 982                 Log
.e("ASD", "W " + options
.outWidth 
+ " SW " + options
.outHeight
); 
 984             } catch (OutOfMemoryError e
) { 
 986                 Log
.e(TAG
, "Out of memory occured for file with size " + storagePath
); 
 988             } catch (NoSuchFieldError e
) { 
 990                 Log
.e(TAG
, "Error from access to unexisting field despite protection " + storagePath
); 
 992             } catch (Throwable t
) { 
 994                 Log
.e(TAG
, "Unexpected error while creating image preview " + storagePath
, t
); 
 999         protected void onPostExecute(Bitmap result
) { 
1000             if (result 
!= null 
&& mPreview 
!= null
) { 
1001                 mPreview
.setImageBitmap(result
);