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