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