35995799e8b61dfbbe0b51a57ea8c5130cd34a44
[pub/Android/ownCloud.git] / src / eu / alefzero / owncloud / Uploader.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 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.URLEncoder;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Stack;
27 import java.util.Vector;
28
29 import android.accounts.Account;
30 import android.accounts.AccountManager;
31 import android.app.AlertDialog;
32 import android.app.Dialog;
33 import android.app.ListActivity;
34 import android.app.ProgressDialog;
35 import android.app.AlertDialog.Builder;
36 import android.content.Context;
37 import android.content.DialogInterface;
38 import android.content.Intent;
39 import android.content.DialogInterface.OnCancelListener;
40 import android.content.DialogInterface.OnClickListener;
41 import android.database.Cursor;
42 import android.net.Uri;
43 import android.os.Bundle;
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.widget.AdapterView;
50 import android.widget.Button;
51 import android.widget.EditText;
52 import android.widget.ListView;
53 import android.widget.SimpleAdapter;
54 import android.widget.SimpleCursorAdapter;
55 import android.widget.AdapterView.OnItemClickListener;
56 import android.widget.Toast;
57 import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
58 import eu.alefzero.owncloud.datamodel.DataStorageManager;
59 import eu.alefzero.owncloud.datamodel.FileDataStorageManager;
60 import eu.alefzero.owncloud.datamodel.OCFile;
61 import eu.alefzero.owncloud.db.ProviderMeta;
62 import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
63 import eu.alefzero.owncloud.files.services.FileUploader;
64 import eu.alefzero.owncloud.utils.OwnCloudVersion;
65 import eu.alefzero.webdav.WebdavClient;
66
67 /**
68 * This can be used to upload things to an ownCloud instance.
69 *
70 * @author Bartek Przybylski
71 *
72 */
73 public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {
74 private static final String TAG = "ownCloudUploader";
75
76 private Account mAccount;
77 private AccountManager mAccountManager;
78 private String mUsername, mPassword;
79 private Stack<String> mParents;
80 private ArrayList<Parcelable> mStreamsToUpload;
81 private boolean mCreateDir;
82 private String mUploadPath;
83 private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
84 private DataStorageManager mStorageManager;
85 private OCFile mFile;
86
87 private final static int DIALOG_NO_ACCOUNT = 0;
88 private final static int DIALOG_WAITING = 1;
89 private final static int DIALOG_NO_STREAM = 2;
90 private final static int DIALOG_MULTIPLE_ACCOUNT = 3;
91 private final static int DIALOG_GET_DIRNAME = 4;
92
93 private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
94
95 @Override
96 protected void onCreate(Bundle savedInstanceState) {
97 super.onCreate(savedInstanceState);
98 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
99 mParents = new Stack<String>();
100 mParents.add("");
101 if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
102 prepareStreamsToUpload();
103 mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
104 Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
105 if (accounts.length == 0) {
106 Log.i(TAG, "No ownCloud account is available");
107 showDialog(DIALOG_NO_ACCOUNT);
108 } else if (accounts.length > 1) {
109 Log.i(TAG, "More then one ownCloud is available");
110 showDialog(DIALOG_MULTIPLE_ACCOUNT);
111 } else {
112 mAccount = accounts[0];
113 setContentView(R.layout.uploader_layout);
114 mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
115 populateDirectoryList();
116 }
117 } else {
118 showDialog(DIALOG_NO_STREAM);
119 }
120 }
121
122 @Override
123 protected Dialog onCreateDialog(final int id) {
124 final AlertDialog.Builder builder = new Builder(this);
125 switch (id) {
126 case DIALOG_WAITING:
127 ProgressDialog pDialog = new ProgressDialog(this);
128 pDialog.setIndeterminate(false);
129 pDialog.setCancelable(false);
130 pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading));
131 return pDialog;
132 case DIALOG_NO_ACCOUNT:
133 builder.setIcon(android.R.drawable.ic_dialog_alert);
134 builder.setTitle(R.string.uploader_wrn_no_account_title);
135 builder.setMessage(R.string.uploader_wrn_no_account_text);
136 builder.setCancelable(false);
137 builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
138 public void onClick(DialogInterface dialog, int which) {
139 if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
140 // using string value since in API7 this
141 // constatn is not defined
142 // in API7 < this constatant is defined in
143 // Settings.ADD_ACCOUNT_SETTINGS
144 // and Settings.EXTRA_AUTHORITIES
145 Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
146 intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
147 startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
148 } else {
149 // since in API7 there is no direct call for
150 // account setup, so we need to
151 // show our own AccountSetupAcricity, get
152 // desired results and setup
153 // everything for ourself
154 Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
155 startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
156 }
157 }
158 });
159 builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {
160 public void onClick(DialogInterface dialog, int which) {
161 finish();
162 }
163 });
164 return builder.create();
165 /*case DIALOG_GET_DIRNAME:
166 final EditText dirName = new EditText(getBaseContext());
167 builder.setView(dirName);
168 builder.setTitle(R.string.uploader_info_dirname);
169 String pathToUpload;
170 if (mParents.empty()) {
171 pathToUpload = "/";
172 } else {
173 mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), null,
174 null, null, null);
175 mCursor.moveToFirst();
176 pathToUpload = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH))
177 + mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)).replace(" ", "%20");
178 }
179 a a = new a(pathToUpload, dirName);
180 builder.setPositiveButton(R.string.common_ok, a);
181 builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
182 public void onClick(DialogInterface dialog, int which) {
183 dialog.cancel();
184 }
185 });
186 return builder.create();*/
187 case DIALOG_MULTIPLE_ACCOUNT:
188 CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];
189 for (int i = 0; i < ac.length; ++i) {
190 ac[i] = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[i].name;
191 }
192 builder.setTitle(R.string.common_choose_account);
193 builder.setItems(ac, new OnClickListener() {
194 public void onClick(DialogInterface dialog, int which) {
195 mAccount = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[which];
196 mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
197 populateDirectoryList();
198 }
199 });
200 builder.setCancelable(true);
201 builder.setOnCancelListener(new OnCancelListener() {
202 public void onCancel(DialogInterface dialog) {
203 dialog.cancel();
204 finish();
205 }
206 });
207 return builder.create();
208 default:
209 throw new IllegalArgumentException("Unknown dialog id: " + id);
210 }
211 }
212
213 class a implements OnClickListener {
214 String mPath;
215 EditText mDirname;
216
217 public a(String path, EditText dirname) {
218 mPath = path;
219 mDirname = dirname;
220 }
221
222 public void onClick(DialogInterface dialog, int which) {
223 Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
224 Uploader.this.mCreateDir = true;
225 uploadFiles();
226 }
227 }
228
229 @Override
230 public void onBackPressed() {
231
232 if (mParents.size() <= 1) {
233 super.onBackPressed();
234 return;
235 } else {
236 mParents.pop();
237 populateDirectoryList();
238 }
239 }
240
241 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
242 Log.d(TAG, "on item click");
243 Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
244 if (tmpfiles == null) return;
245 // filter on dirtype
246 Vector<OCFile> files = new Vector<OCFile>();
247 for (OCFile f : tmpfiles)
248 if (f.isDirectory())
249 files.add(f);
250 if (files.size() < position) {
251 throw new IndexOutOfBoundsException("Incorrect item selected");
252 }
253 mParents.push(files.get(position).getFileName());
254 populateDirectoryList();
255 }
256
257 public void onClick(View v) {
258 switch (v.getId()) {
259 case R.id.uploader_choose_folder:
260 mUploadPath = "";
261 for (String p : mParents)
262 mUploadPath += URLEncoder.encode(p) + "/";
263 Log.d(TAG, "Uploading file to dir " + mUploadPath);
264
265 uploadFiles();
266
267 break;
268 case android.R.id.button1: // dynamic action for create aditional dir
269 showDialog(DIALOG_GET_DIRNAME);
270 break;
271 default:
272 throw new IllegalArgumentException("Wrong element clicked");
273 }
274 }
275
276 @Override
277 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
278 super.onActivityResult(requestCode, resultCode, data);
279 Log.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
280 if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
281 dismissDialog(DIALOG_NO_ACCOUNT);
282 if (resultCode == RESULT_CANCELED) {
283 finish();
284 }
285 Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.AUTH_TOKEN_TYPE);
286 if (accounts.length == 0) {
287 showDialog(DIALOG_NO_ACCOUNT);
288 } else {
289 // there is no need for checking for is there more then one
290 // account at this point
291 // since account setup can set only one account at time
292 mAccount = accounts[0];
293 populateDirectoryList();
294 }
295 }
296 }
297
298 private void populateDirectoryList() {
299 mUsername = mAccount.name.substring(0, mAccount.name.indexOf('@'));
300 mPassword = mAccountManager.getPassword(mAccount);
301 setContentView(R.layout.uploader_layout);
302
303 String full_path = "";
304 for (String a : mParents)
305 full_path += a + "/";
306
307 Log.d(TAG, "Populating view with content of : " + full_path);
308
309 mFile = mStorageManager.getFileByPath(full_path);
310 if (mFile != null) {
311 Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);
312 if (files != null) {
313 List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
314 for (OCFile f : files) {
315 HashMap<String, Object> h = new HashMap<String, Object>();
316 if (f.isDirectory()) {
317 h.put("dirname", f.getFileName());
318 data.add(h);
319 }
320 }
321 SimpleAdapter sa = new SimpleAdapter(this,
322 data,
323 R.layout.uploader_list_item_layout,
324 new String[] {"dirname"},
325 new int[] {R.id.textView1});
326 setListAdapter(sa);
327 Button btn = (Button) findViewById(R.id.uploader_choose_folder);
328 btn.setOnClickListener(this);
329 getListView().setOnItemClickListener(this);
330 }
331 }
332 /*
333 mCursor = managedQuery(ProviderMeta.ProviderTableMeta.CONTENT_URI, null, ProviderTableMeta.FILE_NAME
334 + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?", new String[] { "/", mAccount.name }, null);
335
336 if (mCursor.moveToFirst()) {
337 mCursor = managedQuery(
338 ProviderMeta.ProviderTableMeta.CONTENT_URI,
339 null,
340 ProviderTableMeta.FILE_CONTENT_TYPE + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND "
341 + ProviderTableMeta.FILE_PARENT + "=?",
342 new String[] { "DIR", mAccount.name,
343 mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta._ID)) }, null);
344
345 ListView lv = getListView();
346 lv.setOnItemClickListener(this);
347 SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.uploader_list_item_layout, mCursor,
348 new String[] { ProviderTableMeta.FILE_NAME }, new int[] { R.id.textView1 });
349 setListAdapter(sca);
350 Button btn = (Button) findViewById(R.id.uploader_choose_folder);
351 btn.setOnClickListener(this);
352 /*
353 * disable this until new server interaction service wont be created
354 * // insert create new directory for multiple items uploading if
355 * (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
356 * Button createDirBtn = new Button(this);
357 * createDirBtn.setId(android.R.id.button1);
358 * createDirBtn.setText(R.string.uploader_btn_create_dir_text);
359 * createDirBtn.setOnClickListener(this); ((LinearLayout)
360 * findViewById(R.id.linearLayout1)).addView( createDirBtn,
361 * LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); }
362 *
363 }*/
364 }
365
366 private void prepareStreamsToUpload() {
367 if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
368 mStreamsToUpload = new ArrayList<Parcelable>();
369 mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
370 } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
371 mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
372 } else {
373 // unknow action inserted
374 throw new IllegalArgumentException("Unknown action given: " + getIntent().getAction());
375 }
376 }
377
378 public void uploadFiles() {
379 OwnCloudVersion ocv = new OwnCloudVersion(mAccountManager.getUserData(mAccount,
380 AccountAuthenticator.KEY_OC_VERSION));
381 String base_url = mAccountManager.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
382 String webdav_path = AccountUtils.getWebdavPath(ocv);
383 Uri oc_uri = Uri.parse(base_url + webdav_path);
384
385 WebdavClient wdc = new WebdavClient(oc_uri);
386 wdc.setCredentials(mUsername, mPassword);
387 wdc.allowSelfsignedCertificates();
388
389 // create last directory in path if nessesary
390 if (mCreateDir) {
391 wdc.createDirectory(mUploadPath);
392 }
393
394 String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()];
395
396 for (int i = 0; i < mStreamsToUpload.size(); ++i) {
397 Uri uri = (Uri) mStreamsToUpload.get(i);
398 if (uri.getScheme().equals("content")) {
399 Cursor c = getContentResolver().query((Uri) mStreamsToUpload.get(i),
400 CONTENT_PROJECTION,
401 null,
402 null,
403 null);
404
405 if (!c.moveToFirst())
406 continue;
407
408 final String display_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME)),
409 data = c.getString(c.getColumnIndex(Media.DATA));
410 local[i] = data;
411 remote[i] = mUploadPath + display_name;
412 } else if (uri.getScheme().equals("file")) {
413 final File file = new File(Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""));
414 local[i] = file.getAbsolutePath();
415 remote[i] = mUploadPath + file.getName();
416 }
417
418 }
419 Intent intent = new Intent(getApplicationContext(), FileUploader.class);
420 intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
421 intent.putExtra(FileUploader.KEY_LOCAL_FILE, local);
422 intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote);
423 intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
424 startService(intent);
425 finish();
426 }
427
428 }