10d16ab42d88f04a864095f8cfeafc8f8efb5234
[pub/Android/ownCloud.git] / src / eu / alefzero / owncloud / Uploader.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 Bartek Przybylski
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18 package eu.alefzero.owncloud;
19
20 import java.io.File;
21 import java.net.FileNameMap;
22 import java.net.URLConnection;
23 import java.util.ArrayList;
24 import java.util.Stack;
25
26 import android.accounts.Account;
27 import android.accounts.AccountManager;
28 import android.app.AlertDialog;
29 import android.app.Dialog;
30 import android.app.ListActivity;
31 import android.app.ProgressDialog;
32 import android.app.AlertDialog.Builder;
33 import android.content.ContentValues;
34 import android.content.Context;
35 import android.content.DialogInterface;
36 import android.content.Intent;
37 import android.content.DialogInterface.OnCancelListener;
38 import android.content.DialogInterface.OnClickListener;
39 import android.database.Cursor;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.Parcelable;
44 import android.provider.MediaStore.Images.Media;
45 import android.util.Log;
46 import android.view.View;
47 import android.view.Window;
48 import android.view.ViewGroup.LayoutParams;
49 import android.widget.AdapterView;
50 import android.widget.Button;
51 import android.widget.EditText;
52 import android.widget.LinearLayout;
53 import android.widget.ListView;
54 import android.widget.SimpleCursorAdapter;
55 import android.widget.Toast;
56 import android.widget.AdapterView.OnItemClickListener;
57 import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
58 import eu.alefzero.owncloud.db.ProviderMeta;
59 import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
60 import eu.alefzero.owncloud.files.services.FileUploader;
61 import eu.alefzero.owncloud.utils.OwnCloudVersion;
62 import eu.alefzero.webdav.WebdavClient;
63 import eu.alefzero.webdav.WebdavUtils;
64
65 /**
66 * This can be used to upload things to an ownCloud instance.
67 *
68 * @author Bartek Przybylski
69 *
70 */
71 public class Uploader extends ListActivity implements OnItemClickListener,
72 android.view.View.OnClickListener {
73 private static final String TAG = "ownCloudUploader";
74
75 private Account mAccount;
76 private AccountManager mAccountManager;
77 private String mUsername, mPassword;
78 private Cursor mCursor;
79 private Stack<String> mParents;
80 private Thread mUploadThread;
81 private Handler mHandler;
82 private ArrayList<Parcelable> mStreamsToUpload;
83 private boolean mCreateDir;
84 private String mUploadPath;
85 private static final String[] CONTENT_PROJECTION = {Media.DATA,
86 Media.DISPLAY_NAME,
87 Media.MIME_TYPE,
88 Media.SIZE};
89
90 private final static int DIALOG_NO_ACCOUNT = 0;
91 private final static int DIALOG_WAITING = 1;
92 private final static int DIALOG_NO_STREAM = 2;
93 private final static int DIALOG_MULTIPLE_ACCOUNT = 3;
94 private final static int DIALOG_GET_DIRNAME = 4;
95
96 private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
97
98 @Override
99 protected void onCreate(Bundle savedInstanceState) {
100 super.onCreate(savedInstanceState);
101 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
102 mParents = new Stack<String>();
103 mHandler = new Handler();
104 if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
105 prepareStreamsToUpload();
106 mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
107 Account[] accounts = mAccountManager
108 .getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
109 if (accounts.length == 0) {
110 Log.i(TAG, "No ownCloud account is available");
111 showDialog(DIALOG_NO_ACCOUNT);
112 } else if (accounts.length > 1) {
113 Log.i(TAG, "More then one ownCloud is available");
114 showDialog(DIALOG_MULTIPLE_ACCOUNT);
115 } else {
116 mAccount = accounts[0];
117 setContentView(R.layout.uploader_layout);
118 populateDirectoryList();
119 }
120 } else {
121 showDialog(DIALOG_NO_STREAM);
122 }
123 }
124
125 @Override
126 protected Dialog onCreateDialog(final int id) {
127 final AlertDialog.Builder builder = new Builder(this);
128 switch (id) {
129 case DIALOG_WAITING:
130 ProgressDialog pDialog = new ProgressDialog(this);
131 pDialog.setIndeterminate(false);
132 pDialog.setCancelable(false);
133 pDialog.setMessage(getResources().getString(
134 R.string.uploader_info_uploading));
135 return pDialog;
136 case DIALOG_NO_ACCOUNT:
137 builder.setIcon(android.R.drawable.ic_dialog_alert);
138 builder.setTitle(R.string.uploader_wrn_no_account_title);
139 builder.setMessage(R.string.uploader_wrn_no_account_text);
140 builder.setCancelable(false);
141 builder.setPositiveButton(
142 R.string.uploader_wrn_no_account_setup_btn_text,
143 new OnClickListener() {
144 public void onClick(DialogInterface dialog, int which) {
145 if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
146 // using string value since in API7 this
147 // constatn is not defined
148 // in API7 < this constatant is defined in
149 // Settings.ADD_ACCOUNT_SETTINGS
150 // and Settings.EXTRA_AUTHORITIES
151 Intent intent = new Intent(
152 "android.settings.ADD_ACCOUNT_SETTINGS");
153 intent.putExtra(
154 "authorities",
155 new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
156 startActivityForResult(intent,
157 REQUEST_CODE_SETUP_ACCOUNT);
158 } else {
159 // since in API7 there is no direct call for
160 // account setup, so we need to
161 // show our own AccountSetupAcricity, get
162 // desired results and setup
163 // everything for ourself
164 Intent intent = new Intent(getBaseContext(),
165 AccountAuthenticator.class);
166 startActivityForResult(intent,
167 REQUEST_CODE_SETUP_ACCOUNT);
168 }
169 }
170 });
171 builder.setNegativeButton(
172 R.string.uploader_wrn_no_account_quit_btn_text,
173 new OnClickListener() {
174 public void onClick(DialogInterface dialog, int which) {
175 finish();
176 }
177 });
178 return builder.create();
179 case DIALOG_GET_DIRNAME:
180 final EditText dirName = new EditText(getBaseContext());
181 builder.setView(dirName);
182 builder.setTitle(R.string.uploader_info_dirname);
183 String pathToUpload;
184 if (mParents.empty()) {
185 pathToUpload = "/";
186 } else {
187 mCursor = managedQuery(Uri.withAppendedPath(
188 ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()),
189 null, null, null, null);
190 mCursor.moveToFirst();
191 pathToUpload = mCursor.getString(mCursor
192 .getColumnIndex(ProviderTableMeta.FILE_PATH))
193 + mCursor
194 .getString(
195 mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME))
196 .replace(" ", "%20");
197 }
198 a a = new a(pathToUpload, dirName);
199 builder.setPositiveButton(R.string.common_ok, a);
200 builder.setNegativeButton(R.string.common_cancel,
201 new OnClickListener() {
202 public void onClick(DialogInterface dialog, int which) {
203 dialog.cancel();
204 }
205 });
206 return builder.create();
207 case DIALOG_MULTIPLE_ACCOUNT:
208 CharSequence ac[] = new CharSequence[mAccountManager
209 .getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];
210 for (int i = 0; i < ac.length; ++i) {
211 ac[i] = mAccountManager
212 .getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[i].name;
213 }
214 builder.setTitle(R.string.common_choose_account);
215 builder.setItems(ac, new OnClickListener() {
216 public void onClick(DialogInterface dialog, int which) {
217 mAccount = mAccountManager
218 .getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[which];
219 populateDirectoryList();
220 }
221 });
222 builder.setCancelable(true);
223 builder.setOnCancelListener(new OnCancelListener() {
224 public void onCancel(DialogInterface dialog) {
225 dialog.cancel();
226 finish();
227 }
228 });
229 return builder.create();
230 default:
231 throw new IllegalArgumentException("Unknown dialog id: " + id);
232 }
233 }
234
235 class a implements OnClickListener {
236 String mPath;
237 EditText mDirname;
238
239 public a(String path, EditText dirname) {
240 mPath = path;
241 mDirname = dirname;
242 }
243
244 public void onClick(DialogInterface dialog, int which) {
245 Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
246 Uploader.this.mCreateDir = true;
247 uploadFiles();
248 }
249 }
250
251 @Override
252 public void onBackPressed() {
253
254 if (mParents.size() == 0) {
255 super.onBackPressed();
256 return;
257 } else if (mParents.size() == 1) {
258 mParents.pop();
259 mCursor = managedQuery(ProviderTableMeta.CONTENT_URI, null,
260 ProviderTableMeta.FILE_CONTENT_TYPE + "=?",
261 new String[] { "DIR" }, null);
262 } else {
263 mParents.pop();
264 mCursor = managedQuery(Uri.withAppendedPath(
265 ProviderTableMeta.CONTENT_URI_DIR, mParents.peek()), null,
266 ProviderTableMeta.FILE_CONTENT_TYPE + "=?",
267 new String[] { "DIR" }, null);
268 }
269
270 SimpleCursorAdapter sca = new SimpleCursorAdapter(this,
271 R.layout.uploader_list_item_layout, mCursor,
272 new String[] { ProviderTableMeta.FILE_NAME },
273 new int[] { R.id.textView1 });
274 setListAdapter(sca);
275 }
276
277 public void onItemClick(AdapterView<?> parent, View view, int position,
278 long id) {
279 if (!mCursor.moveToPosition(position)) {
280 throw new IndexOutOfBoundsException("Incorrect item selected");
281 }
282 String _id = mCursor.getString(mCursor
283 .getColumnIndex(ProviderTableMeta._ID));
284 mParents.push(_id);
285
286 mCursor.close();
287 mCursor = managedQuery(
288 Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, _id),
289 null, ProviderTableMeta.FILE_CONTENT_TYPE + "=?",
290 new String[] { "DIR" }, null);
291 SimpleCursorAdapter sca = new SimpleCursorAdapter(this,
292 R.layout.uploader_list_item_layout, mCursor,
293 new String[] { ProviderTableMeta.FILE_NAME },
294 new int[] { R.id.textView1 });
295 setListAdapter(sca);
296 getListView().invalidate();
297 }
298
299 public void onClick(View v) {
300 switch (v.getId()) {
301 case R.id.uploader_choose_folder:
302 String pathToUpload = null;
303 if (mParents.empty()) {
304 pathToUpload = "/";
305 } else {
306 mCursor = managedQuery(Uri.withAppendedPath(
307 ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()),
308 null, null, null, null);
309 mCursor.moveToFirst();
310 pathToUpload = mCursor.getString(
311 mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH))
312 .replace(" ", "%20");
313 }
314 Log.d(TAG, "Uploading file to dir " + pathToUpload);
315
316 mUploadPath = pathToUpload;
317 mCreateDir = false;
318 uploadFiles();
319
320 break;
321 case android.R.id.button1: // dynamic action for create aditional dir
322 showDialog(DIALOG_GET_DIRNAME);
323 break;
324 default:
325 throw new IllegalArgumentException("Wrong element clicked");
326 }
327 }
328
329 public void onUploadComplete(boolean uploadSucc, String message) {
330 dismissDialog(DIALOG_WAITING);
331 Log.i(TAG, "UploadSucc: " + uploadSucc + " message: " + message);
332 if (uploadSucc) {
333 Toast.makeText(this,
334 getResources().getString(R.string.uploader_upload_succeed),
335 Toast.LENGTH_SHORT).show();
336 } else {
337 Toast.makeText(
338 this,
339 getResources().getString(R.string.uploader_upload_failed)
340 + message, Toast.LENGTH_LONG).show();
341 }
342 finish();
343 }
344
345 @Override
346 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
347 super.onActivityResult(requestCode, resultCode, data);
348 Log.i(TAG, "result received. req: " + requestCode + " res: "
349 + resultCode);
350 if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
351 dismissDialog(DIALOG_NO_ACCOUNT);
352 if (resultCode == RESULT_CANCELED) {
353 finish();
354 }
355 Account[] accounts = mAccountManager
356 .getAccountsByType(AccountAuthenticator.AUTH_TOKEN_TYPE);
357 if (accounts.length == 0) {
358 showDialog(DIALOG_NO_ACCOUNT);
359 } else {
360 // there is no need for checking for is there more then one
361 // account at this point
362 // since account setup can set only one account at time
363 mAccount = accounts[0];
364 populateDirectoryList();
365 }
366 }
367 }
368
369 private void populateDirectoryList() {
370 mUsername = mAccount.name.substring(0, mAccount.name.indexOf('@'));
371 mPassword = mAccountManager.getPassword(mAccount);
372 setContentView(R.layout.uploader_layout);
373
374 mCursor = managedQuery(ProviderMeta.ProviderTableMeta.CONTENT_URI,
375 null, ProviderTableMeta.FILE_NAME + "=? AND "
376 + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
377 new String[] { "/", mAccount.name }, null);
378
379 if (mCursor.moveToFirst()) {
380 mCursor = managedQuery(
381 ProviderMeta.ProviderTableMeta.CONTENT_URI,
382 null,
383 ProviderTableMeta.FILE_CONTENT_TYPE + "=? AND "
384 + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND "
385 + ProviderTableMeta.FILE_PARENT + "=?",
386 new String[] {
387 "DIR",
388 mAccount.name,
389 mCursor.getString(mCursor
390 .getColumnIndex(ProviderTableMeta._ID)) },
391 null);
392
393 ListView lv = getListView();
394 lv.setOnItemClickListener(this);
395 SimpleCursorAdapter sca = new SimpleCursorAdapter(this,
396 R.layout.uploader_list_item_layout, mCursor,
397 new String[] { ProviderTableMeta.FILE_NAME },
398 new int[] { R.id.textView1 });
399 setListAdapter(sca);
400 Button btn = (Button) findViewById(R.id.uploader_choose_folder);
401 btn.setOnClickListener(this);
402 /* disable this until new server interaction service wont be created
403 // insert create new directory for multiple items uploading
404 if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
405 Button createDirBtn = new Button(this);
406 createDirBtn.setId(android.R.id.button1);
407 createDirBtn.setText(R.string.uploader_btn_create_dir_text);
408 createDirBtn.setOnClickListener(this);
409 ((LinearLayout) findViewById(R.id.linearLayout1)).addView(
410 createDirBtn, LayoutParams.FILL_PARENT,
411 LayoutParams.WRAP_CONTENT);
412 }*/
413 }
414 }
415
416 private void prepareStreamsToUpload() {
417 if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
418 mStreamsToUpload = new ArrayList<Parcelable>();
419 mStreamsToUpload.add(getIntent().getParcelableExtra(
420 Intent.EXTRA_STREAM));
421 } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
422 mStreamsToUpload = getIntent().getParcelableArrayListExtra(
423 Intent.EXTRA_STREAM);
424 } else {
425 // unknow action inserted
426 throw new IllegalArgumentException("Unknown action given: "
427 + getIntent().getAction());
428 }
429 }
430
431 public void PartialupdateUpload(String fileLocalPath, String filename,
432 String filepath, String contentType, String contentLength) {
433 ContentValues cv = new ContentValues();
434 cv.put(ProviderTableMeta.FILE_NAME, filename);
435 cv.put(ProviderTableMeta.FILE_PATH, filepath);
436 cv.put(ProviderTableMeta.FILE_STORAGE_PATH, fileLocalPath);
437 cv.put(ProviderTableMeta.FILE_MODIFIED,
438 WebdavUtils.DISPLAY_DATE_FORMAT.format(new java.util.Date()));
439 cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, contentType);
440 cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, contentLength);
441 cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
442 Log.d(TAG, filename + " ++ " + filepath + " ++ " + contentLength
443 + " ++ " + contentType + " ++ " + fileLocalPath);
444 if (!mParents.empty()) {
445 Cursor c = managedQuery(Uri.withAppendedPath(
446 ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), null,
447 null, null, null);
448 c.moveToFirst();
449 cv.put(ProviderTableMeta.FILE_PARENT,
450 c.getString(c.getColumnIndex(ProviderTableMeta._ID)));
451 c.close();
452 }
453 getContentResolver().insert(ProviderTableMeta.CONTENT_URI_FILE, cv);
454 }
455
456 public void uploadFiles() {
457 OwnCloudVersion ocv = new OwnCloudVersion(mAccountManager
458 .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
459 String base_url = mAccountManager.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
460 String webdav_path = AccountUtils.getWebdavPath(ocv);
461 Uri oc_uri = Uri.parse(base_url+webdav_path);
462
463 WebdavClient wdc = new WebdavClient(oc_uri);
464 wdc.setCredentials(mUsername, mPassword);
465 wdc.allowUnsignedCertificates();
466
467 // create last directory in path if nessesary
468 if (mCreateDir) {
469 wdc.createDirectory(mUploadPath);
470 }
471
472 String[] local = new String[mStreamsToUpload.size()],
473 remote = new String[mStreamsToUpload.size()];
474
475 for (int i = 0; i < mStreamsToUpload.size(); ++i) {
476 Uri uri = (Uri) mStreamsToUpload.get(i);
477 if (uri.getScheme().equals("content")) {
478 Cursor c = getContentResolver().query((Uri) mStreamsToUpload.get(i),
479 CONTENT_PROJECTION,
480 null,
481 null,
482 null);
483
484 if (!c.moveToFirst()) continue;
485
486 final String display_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME)),
487 data = c.getString(c.getColumnIndex(Media.DATA));
488 local[i] = data;
489 remote[i] = mUploadPath + "/" + display_name;
490
491 } else if (uri.getScheme().equals("file")) {
492 final File file = new File(Uri.decode(uri.toString())
493 .replace(uri.getScheme() + "://", ""));
494 FileNameMap fileNameMap = URLConnection.getFileNameMap();
495 String contentType = fileNameMap.getContentTypeFor(uri
496 .toString());
497 if (contentType == null) {
498 contentType = "text/plain";
499 }
500 if (!wdc.putFile(file.getAbsolutePath(), mUploadPath + "/"
501 + file.getName(), contentType)) {
502 mHandler.post(new Runnable() {
503 public void run() {
504 Uploader.this.onUploadComplete(
505 false,
506 "Error while uploading file: "
507 + file.getName());
508 }
509 });
510 }
511 }
512
513 }
514 Intent intent = new Intent(getApplicationContext(), FileUploader.class);
515 intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
516 intent.putExtra(FileUploader.KEY_LOCAL_FILE, local);
517 intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote);
518 intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
519 startService(intent);
520 finish();
521 }
522
523
524
525 }