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