1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012  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
; 
  21 import java
.util
.ArrayList
; 
  22 import java
.util
.Stack
; 
  24 import android
.accounts
.Account
; 
  25 import android
.accounts
.AccountManager
; 
  26 import android
.app
.AlertDialog
; 
  27 import android
.app
.Dialog
; 
  28 import android
.app
.ListActivity
; 
  29 import android
.app
.ProgressDialog
; 
  30 import android
.app
.AlertDialog
.Builder
; 
  31 import android
.content
.Context
; 
  32 import android
.content
.DialogInterface
; 
  33 import android
.content
.Intent
; 
  34 import android
.content
.DialogInterface
.OnCancelListener
; 
  35 import android
.content
.DialogInterface
.OnClickListener
; 
  36 import android
.database
.Cursor
; 
  37 import android
.net
.Uri
; 
  38 import android
.os
.Bundle
; 
  39 import android
.os
.Parcelable
; 
  40 import android
.provider
.MediaStore
.Images
.Media
; 
  41 import android
.util
.Log
; 
  42 import android
.view
.View
; 
  43 import android
.view
.Window
; 
  44 import android
.widget
.AdapterView
; 
  45 import android
.widget
.Button
; 
  46 import android
.widget
.EditText
; 
  47 import android
.widget
.ListView
; 
  48 import android
.widget
.SimpleCursorAdapter
; 
  49 import android
.widget
.AdapterView
.OnItemClickListener
; 
  50 import eu
.alefzero
.owncloud
.authenticator
.AccountAuthenticator
; 
  51 import eu
.alefzero
.owncloud
.db
.ProviderMeta
; 
  52 import eu
.alefzero
.owncloud
.db
.ProviderMeta
.ProviderTableMeta
; 
  53 import eu
.alefzero
.owncloud
.files
.services
.FileUploader
; 
  54 import eu
.alefzero
.owncloud
.utils
.OwnCloudVersion
; 
  55 import eu
.alefzero
.webdav
.WebdavClient
; 
  58  * This can be used to upload things to an ownCloud instance. 
  60  * @author Bartek Przybylski 
  63 public class Uploader 
extends ListActivity 
implements OnItemClickListener
, android
.view
.View
.OnClickListener 
{ 
  64     private static final String TAG 
= "ownCloudUploader"; 
  66     private Account mAccount
; 
  67     private AccountManager mAccountManager
; 
  68     private String mUsername
, mPassword
; 
  69     private Cursor mCursor
; 
  70     private Stack
<String
> mParents
; 
  71     private ArrayList
<Parcelable
> mStreamsToUpload
; 
  72     private boolean mCreateDir
; 
  73     private String mUploadPath
; 
  74     private static final String
[] CONTENT_PROJECTION 
= { Media
.DATA
, Media
.DISPLAY_NAME
, Media
.MIME_TYPE
, Media
.SIZE 
}; 
  76     private final static int DIALOG_NO_ACCOUNT 
= 0; 
  77     private final static int DIALOG_WAITING 
= 1; 
  78     private final static int DIALOG_NO_STREAM 
= 2; 
  79     private final static int DIALOG_MULTIPLE_ACCOUNT 
= 3; 
  80     private final static int DIALOG_GET_DIRNAME 
= 4; 
  82     private final static int REQUEST_CODE_SETUP_ACCOUNT 
= 0; 
  85     protected void onCreate(Bundle savedInstanceState
) { 
  86         super.onCreate(savedInstanceState
); 
  87         getWindow().requestFeature(Window
.FEATURE_NO_TITLE
); 
  88         mParents 
= new Stack
<String
>(); 
  89         if (getIntent().hasExtra(Intent
.EXTRA_STREAM
)) { 
  90             prepareStreamsToUpload(); 
  91             mAccountManager 
= (AccountManager
) getSystemService(Context
.ACCOUNT_SERVICE
); 
  92             Account
[] accounts 
= mAccountManager
.getAccountsByType(AccountAuthenticator
.ACCOUNT_TYPE
); 
  93             if (accounts
.length 
== 0) { 
  94                 Log
.i(TAG
, "No ownCloud account is available"); 
  95                 showDialog(DIALOG_NO_ACCOUNT
); 
  96             } else if (accounts
.length 
> 1) { 
  97                 Log
.i(TAG
, "More then one ownCloud is available"); 
  98                 showDialog(DIALOG_MULTIPLE_ACCOUNT
); 
 100                 mAccount 
= accounts
[0]; 
 101                 setContentView(R
.layout
.uploader_layout
); 
 102                 populateDirectoryList(); 
 105             showDialog(DIALOG_NO_STREAM
); 
 110     protected Dialog 
onCreateDialog(final int id
) { 
 111         final AlertDialog
.Builder builder 
= new Builder(this); 
 114             ProgressDialog pDialog 
= new ProgressDialog(this); 
 115             pDialog
.setIndeterminate(false
); 
 116             pDialog
.setCancelable(false
); 
 117             pDialog
.setMessage(getResources().getString(R
.string
.uploader_info_uploading
)); 
 119         case DIALOG_NO_ACCOUNT
: 
 120             builder
.setIcon(android
.R
.drawable
.ic_dialog_alert
); 
 121             builder
.setTitle(R
.string
.uploader_wrn_no_account_title
); 
 122             builder
.setMessage(R
.string
.uploader_wrn_no_account_text
); 
 123             builder
.setCancelable(false
); 
 124             builder
.setPositiveButton(R
.string
.uploader_wrn_no_account_setup_btn_text
, new OnClickListener() { 
 125                 public void onClick(DialogInterface dialog
, int which
) { 
 126                     if (android
.os
.Build
.VERSION
.SDK_INT 
> android
.os
.Build
.VERSION_CODES
.ECLAIR_MR1
) { 
 127                         // using string value since in API7 this 
 128                         // constatn is not defined 
 129                         // in API7 < this constatant is defined in 
 130                         // Settings.ADD_ACCOUNT_SETTINGS 
 131                         // and Settings.EXTRA_AUTHORITIES 
 132                         Intent intent 
= new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); 
 133                         intent
.putExtra("authorities", new String
[] { AccountAuthenticator
.AUTH_TOKEN_TYPE 
}); 
 134                         startActivityForResult(intent
, REQUEST_CODE_SETUP_ACCOUNT
); 
 136                         // since in API7 there is no direct call for 
 137                         // account setup, so we need to 
 138                         // show our own AccountSetupAcricity, get 
 139                         // desired results and setup 
 140                         // everything for ourself 
 141                         Intent intent 
= new Intent(getBaseContext(), AccountAuthenticator
.class); 
 142                         startActivityForResult(intent
, REQUEST_CODE_SETUP_ACCOUNT
); 
 146             builder
.setNegativeButton(R
.string
.uploader_wrn_no_account_quit_btn_text
, new OnClickListener() { 
 147                 public void onClick(DialogInterface dialog
, int which
) { 
 151             return builder
.create(); 
 152         case DIALOG_GET_DIRNAME
: 
 153             final EditText dirName 
= new EditText(getBaseContext()); 
 154             builder
.setView(dirName
); 
 155             builder
.setTitle(R
.string
.uploader_info_dirname
); 
 157             if (mParents
.empty()) { 
 160                 mCursor 
= managedQuery(Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_FILE
, mParents
.peek()), null
, 
 162                 mCursor
.moveToFirst(); 
 163                 pathToUpload 
= mCursor
.getString(mCursor
.getColumnIndex(ProviderTableMeta
.FILE_PATH
)) 
 164                         + mCursor
.getString(mCursor
.getColumnIndex(ProviderTableMeta
.FILE_NAME
)).replace(" ", "%20"); 
 166             a a 
= new a(pathToUpload
, dirName
); 
 167             builder
.setPositiveButton(R
.string
.common_ok
, a
); 
 168             builder
.setNegativeButton(R
.string
.common_cancel
, new OnClickListener() { 
 169                 public void onClick(DialogInterface dialog
, int which
) { 
 173             return builder
.create(); 
 174         case DIALOG_MULTIPLE_ACCOUNT
: 
 175             CharSequence ac
[] = new CharSequence
[mAccountManager
.getAccountsByType(AccountAuthenticator
.ACCOUNT_TYPE
).length
]; 
 176             for (int i 
= 0; i 
< ac
.length
; ++i
) { 
 177                 ac
[i
] = mAccountManager
.getAccountsByType(AccountAuthenticator
.ACCOUNT_TYPE
)[i
].name
; 
 179             builder
.setTitle(R
.string
.common_choose_account
); 
 180             builder
.setItems(ac
, new OnClickListener() { 
 181                 public void onClick(DialogInterface dialog
, int which
) { 
 182                     mAccount 
= mAccountManager
.getAccountsByType(AccountAuthenticator
.ACCOUNT_TYPE
)[which
]; 
 183                     populateDirectoryList(); 
 186             builder
.setCancelable(true
); 
 187             builder
.setOnCancelListener(new OnCancelListener() { 
 188                 public void onCancel(DialogInterface dialog
) { 
 193             return builder
.create(); 
 195             throw new IllegalArgumentException("Unknown dialog id: " + id
); 
 199     class a 
implements OnClickListener 
{ 
 203         public a(String path
, EditText dirname
) { 
 208         public void onClick(DialogInterface dialog
, int which
) { 
 209             Uploader
.this.mUploadPath 
= mPath 
+ mDirname
.getText().toString(); 
 210             Uploader
.this.mCreateDir 
= true
; 
 216     public void onBackPressed() { 
 218         if (mParents
.size() == 0) { 
 219             super.onBackPressed(); 
 221         } else if (mParents
.size() == 1) { 
 223             mCursor 
= managedQuery(ProviderTableMeta
.CONTENT_URI
, null
, ProviderTableMeta
.FILE_CONTENT_TYPE 
+ "=?", 
 224                     new String
[] { "DIR" }, null
); 
 227             mCursor 
= managedQuery(Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_DIR
, mParents
.peek()), null
, 
 228                     ProviderTableMeta
.FILE_CONTENT_TYPE 
+ "=?", new String
[] { "DIR" }, null
); 
 231         SimpleCursorAdapter sca 
= new SimpleCursorAdapter(this, R
.layout
.uploader_list_item_layout
, mCursor
, 
 232                 new String
[] { ProviderTableMeta
.FILE_NAME 
}, new int[] { R
.id
.textView1 
}); 
 236     public void onItemClick(AdapterView
<?
> parent
, View view
, int position
, long id
) { 
 237         if (!mCursor
.moveToPosition(position
)) { 
 238             throw new IndexOutOfBoundsException("Incorrect item selected"); 
 240         String _id 
= mCursor
.getString(mCursor
.getColumnIndex(ProviderTableMeta
._ID
)); 
 244         mCursor 
= managedQuery(Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_DIR
, _id
), null
, 
 245                 ProviderTableMeta
.FILE_CONTENT_TYPE 
+ "=?", new String
[] { "DIR" }, null
); 
 246         SimpleCursorAdapter sca 
= new SimpleCursorAdapter(this, R
.layout
.uploader_list_item_layout
, mCursor
, 
 247                 new String
[] { ProviderTableMeta
.FILE_NAME 
}, new int[] { R
.id
.textView1 
}); 
 249         getListView().invalidate(); 
 252     public void onClick(View v
) { 
 254         case R
.id
.uploader_choose_folder
: 
 255             String pathToUpload 
= null
; 
 256             if (mParents
.empty()) { 
 259                 mCursor 
= managedQuery(Uri
.withAppendedPath(ProviderTableMeta
.CONTENT_URI_FILE
, mParents
.peek()), null
, 
 261                 mCursor
.moveToFirst(); 
 262                 pathToUpload 
= mCursor
.getString(mCursor
.getColumnIndex(ProviderTableMeta
.FILE_PATH
)).replace(" ", 
 265             Log
.d(TAG
, "Uploading file to dir " + pathToUpload
); 
 267             mUploadPath 
= pathToUpload
; 
 272         case android
.R
.id
.button1
: // dynamic action for create aditional dir 
 273             showDialog(DIALOG_GET_DIRNAME
); 
 276             throw new IllegalArgumentException("Wrong element clicked"); 
 281     protected void onActivityResult(int requestCode
, int resultCode
, Intent data
) { 
 282         super.onActivityResult(requestCode
, resultCode
, data
); 
 283         Log
.i(TAG
, "result received. req: " + requestCode 
+ " res: " + resultCode
); 
 284         if (requestCode 
== REQUEST_CODE_SETUP_ACCOUNT
) { 
 285             dismissDialog(DIALOG_NO_ACCOUNT
); 
 286             if (resultCode 
== RESULT_CANCELED
) { 
 289             Account
[] accounts 
= mAccountManager
.getAccountsByType(AccountAuthenticator
.AUTH_TOKEN_TYPE
); 
 290             if (accounts
.length 
== 0) { 
 291                 showDialog(DIALOG_NO_ACCOUNT
); 
 293                 // there is no need for checking for is there more then one 
 294                 // account at this point 
 295                 // since account setup can set only one account at time 
 296                 mAccount 
= accounts
[0]; 
 297                 populateDirectoryList(); 
 302     private void populateDirectoryList() { 
 303         mUsername 
= mAccount
.name
.substring(0, mAccount
.name
.indexOf('@')); 
 304         mPassword 
= mAccountManager
.getPassword(mAccount
); 
 305         setContentView(R
.layout
.uploader_layout
); 
 307         mCursor 
= managedQuery(ProviderMeta
.ProviderTableMeta
.CONTENT_URI
, null
, ProviderTableMeta
.FILE_NAME
 
 308                 + "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=?", new String
[] { "/", mAccount
.name 
}, null
); 
 310         if (mCursor
.moveToFirst()) { 
 311             mCursor 
= managedQuery( 
 312                     ProviderMeta
.ProviderTableMeta
.CONTENT_URI
, 
 314                     ProviderTableMeta
.FILE_CONTENT_TYPE 
+ "=? AND " + ProviderTableMeta
.FILE_ACCOUNT_OWNER 
+ "=? AND " 
 315                             + ProviderTableMeta
.FILE_PARENT 
+ "=?", 
 316                     new String
[] { "DIR", mAccount
.name
, 
 317                             mCursor
.getString(mCursor
.getColumnIndex(ProviderTableMeta
._ID
)) }, null
); 
 319             ListView lv 
= getListView(); 
 320             lv
.setOnItemClickListener(this); 
 321             SimpleCursorAdapter sca 
= new SimpleCursorAdapter(this, R
.layout
.uploader_list_item_layout
, mCursor
, 
 322                     new String
[] { ProviderTableMeta
.FILE_NAME 
}, new int[] { R
.id
.textView1 
}); 
 324             Button btn 
= (Button
) findViewById(R
.id
.uploader_choose_folder
); 
 325             btn
.setOnClickListener(this); 
 327              * disable this until new server interaction service wont be created 
 328              * // insert create new directory for multiple items uploading if 
 329              * (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) { 
 330              * Button createDirBtn = new Button(this); 
 331              * createDirBtn.setId(android.R.id.button1); 
 332              * createDirBtn.setText(R.string.uploader_btn_create_dir_text); 
 333              * createDirBtn.setOnClickListener(this); ((LinearLayout) 
 334              * findViewById(R.id.linearLayout1)).addView( createDirBtn, 
 335              * LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); } 
 340     private void prepareStreamsToUpload() { 
 341         if (getIntent().getAction().equals(Intent
.ACTION_SEND
)) { 
 342             mStreamsToUpload 
= new ArrayList
<Parcelable
>(); 
 343             mStreamsToUpload
.add(getIntent().getParcelableExtra(Intent
.EXTRA_STREAM
)); 
 344         } else if (getIntent().getAction().equals(Intent
.ACTION_SEND_MULTIPLE
)) { 
 345             mStreamsToUpload 
= getIntent().getParcelableArrayListExtra(Intent
.EXTRA_STREAM
); 
 347             // unknow action inserted 
 348             throw new IllegalArgumentException("Unknown action given: " + getIntent().getAction()); 
 352     public void uploadFiles() { 
 353         OwnCloudVersion ocv 
= new OwnCloudVersion(mAccountManager
.getUserData(mAccount
, 
 354                 AccountAuthenticator
.KEY_OC_VERSION
)); 
 355         String base_url 
= mAccountManager
.getUserData(mAccount
, AccountAuthenticator
.KEY_OC_BASE_URL
); 
 356         String webdav_path 
= AccountUtils
.getWebdavPath(ocv
); 
 357         Uri oc_uri 
= Uri
.parse(base_url 
+ webdav_path
); 
 359         WebdavClient wdc 
= new WebdavClient(oc_uri
); 
 360         wdc
.setCredentials(mUsername
, mPassword
); 
 361         wdc
.allowUnsignedCertificates(); 
 363         // create last directory in path if nessesary 
 365             wdc
.createDirectory(mUploadPath
); 
 368         String
[] local 
= new String
[mStreamsToUpload
.size()], remote 
= new String
[mStreamsToUpload
.size()]; 
 370         for (int i 
= 0; i 
< mStreamsToUpload
.size(); ++i
) { 
 371             Uri uri 
= (Uri
) mStreamsToUpload
.get(i
); 
 372             if (uri
.getScheme().equals("content")) { 
 373                 Cursor c 
= getContentResolver().query((Uri
) mStreamsToUpload
.get(i
), 
 379                 if (!c
.moveToFirst()) 
 382                 final String display_name 
= c
.getString(c
.getColumnIndex(Media
.DISPLAY_NAME
)), 
 383                              data 
= c
.getString(c
.getColumnIndex(Media
.DATA
)); 
 385                 remote
[i
] = mUploadPath 
+ "/" + display_name
; 
 386             } else if (uri
.getScheme().equals("file")) { 
 387                 final File file 
= new File(Uri
.decode(uri
.toString()).replace(uri
.getScheme() + "://", "")); 
 388                 local
[i
] = file
.getAbsolutePath(); 
 389                 remote
[i
] = mUploadPath 
+ "/" + file
.getName(); 
 393         Intent intent 
= new Intent(getApplicationContext(), FileUploader
.class); 
 394         intent
.putExtra(FileUploader
.KEY_UPLOAD_TYPE
, FileUploader
.UPLOAD_MULTIPLE_FILES
); 
 395         intent
.putExtra(FileUploader
.KEY_LOCAL_FILE
, local
); 
 396         intent
.putExtra(FileUploader
.KEY_REMOTE_FILE
, remote
); 
 397         intent
.putExtra(FileUploader
.KEY_ACCOUNT
, mAccount
); 
 398         startService(intent
);