Merge tag 'oc-android-1-3-22' into oauth_login
[pub/Android/ownCloud.git] / src / com / owncloud / android / Uploader.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2013 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19 package com.owncloud.android;
20
21 import java.io.File;
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 com.owncloud.android.authenticator.AccountAuthenticator;
30 import com.owncloud.android.datamodel.DataStorageManager;
31 import com.owncloud.android.datamodel.FileDataStorageManager;
32 import com.owncloud.android.datamodel.OCFile;
33 import com.owncloud.android.files.services.FileUploader;
34
35 import android.accounts.Account;
36 import android.accounts.AccountManager;
37 import android.app.AlertDialog;
38 import android.app.AlertDialog.Builder;
39 import android.app.Dialog;
40 import android.app.ListActivity;
41 import android.app.ProgressDialog;
42 import android.content.Context;
43 import android.content.DialogInterface;
44 import android.content.DialogInterface.OnCancelListener;
45 import android.content.DialogInterface.OnClickListener;
46 import android.content.Intent;
47 import android.database.Cursor;
48 import android.net.Uri;
49 import android.os.Bundle;
50 import android.os.Parcelable;
51 import android.provider.MediaStore.Images.Media;
52 import android.util.Log;
53 import android.view.View;
54 import android.view.Window;
55 import android.widget.AdapterView;
56 import android.widget.AdapterView.OnItemClickListener;
57 import android.widget.Button;
58 import android.widget.EditText;
59 import android.widget.SimpleAdapter;
60 import android.widget.Toast;
61
62 import com.owncloud.android.R;
63
64 /**
65 * This can be used to upload things to an ownCloud instance.
66 *
67 * @author Bartek Przybylski
68 *
69 */
70 public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {
71 private static final String TAG = "ownCloudUploader";
72
73 private Account mAccount;
74 private AccountManager mAccountManager;
75 private Stack<String> mParents;
76 private ArrayList<Parcelable> mStreamsToUpload;
77 private boolean mCreateDir;
78 private String mUploadPath;
79 private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
80 private DataStorageManager mStorageManager;
81 private OCFile mFile;
82
83 private final static int DIALOG_NO_ACCOUNT = 0;
84 private final static int DIALOG_WAITING = 1;
85 private final static int DIALOG_NO_STREAM = 2;
86 private final static int DIALOG_MULTIPLE_ACCOUNT = 3;
87 //private final static int DIALOG_GET_DIRNAME = 4;
88
89 private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
90
91 @Override
92 protected void onCreate(Bundle savedInstanceState) {
93 super.onCreate(savedInstanceState);
94 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
95 mParents = new Stack<String>();
96 mParents.add("");
97 /*if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
98 prepareStreamsToUpload();*/
99 if (prepareStreamsToUpload()) {
100 mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
101 Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
102 if (accounts.length == 0) {
103 Log.i(TAG, "No ownCloud account is available");
104 showDialog(DIALOG_NO_ACCOUNT);
105 } else if (accounts.length > 1) {
106 Log.i(TAG, "More then one ownCloud is available");
107 showDialog(DIALOG_MULTIPLE_ACCOUNT);
108 } else {
109 mAccount = accounts[0];
110 mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
111 populateDirectoryList();
112 }
113 } else {
114 showDialog(DIALOG_NO_STREAM);
115 }
116 }
117
118 @Override
119 protected Dialog onCreateDialog(final int id) {
120 final AlertDialog.Builder builder = new Builder(this);
121 switch (id) {
122 case DIALOG_WAITING:
123 ProgressDialog pDialog = new ProgressDialog(this);
124 pDialog.setIndeterminate(false);
125 pDialog.setCancelable(false);
126 pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading));
127 return pDialog;
128 case DIALOG_NO_ACCOUNT:
129 builder.setIcon(android.R.drawable.ic_dialog_alert);
130 builder.setTitle(R.string.uploader_wrn_no_account_title);
131 builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
132 builder.setCancelable(false);
133 builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
134 @Override
135 public void onClick(DialogInterface dialog, int which) {
136 if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
137 // using string value since in API7 this
138 // constatn is not defined
139 // in API7 < this constatant is defined in
140 // Settings.ADD_ACCOUNT_SETTINGS
141 // and Settings.EXTRA_AUTHORITIES
142 Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
143 intent.putExtra("authorities", new String[] { AccountAuthenticator.AUTHORITY });
144 startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
145 } else {
146 // since in API7 there is no direct call for
147 // account setup, so we need to
148 // show our own AccountSetupAcricity, get
149 // desired results and setup
150 // everything for ourself
151 Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
152 startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
153 }
154 }
155 });
156 builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {
157 @Override
158 public void onClick(DialogInterface dialog, int which) {
159 finish();
160 }
161 });
162 return builder.create();
163 /*case DIALOG_GET_DIRNAME:
164 final EditText dirName = new EditText(getBaseContext());
165 builder.setView(dirName);
166 builder.setTitle(R.string.uploader_info_dirname);
167 String pathToUpload;
168 if (mParents.empty()) {
169 pathToUpload = "/";
170 } else {
171 mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), null,
172 null, null, null);
173 mCursor.moveToFirst();
174 pathToUpload = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH))
175 + mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)).replace(" ", "%20"); // TODO don't make this ; use WebdavUtils.encode in the right moment
176 }
177 a a = new a(pathToUpload, dirName);
178 builder.setPositiveButton(R.string.common_ok, a);
179 builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
180 public void onClick(DialogInterface dialog, int which) {
181 dialog.cancel();
182 }
183 });
184 return builder.create();*/
185 case DIALOG_MULTIPLE_ACCOUNT:
186 CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];
187 for (int i = 0; i < ac.length; ++i) {
188 ac[i] = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[i].name;
189 }
190 builder.setTitle(R.string.common_choose_account);
191 builder.setItems(ac, new OnClickListener() {
192 @Override
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 @Override
202 public void onCancel(DialogInterface dialog) {
203 dialog.cancel();
204 finish();
205 }
206 });
207 return builder.create();
208 case DIALOG_NO_STREAM:
209 builder.setIcon(android.R.drawable.ic_dialog_alert);
210 builder.setTitle(R.string.uploader_wrn_no_content_title);
211 builder.setMessage(R.string.uploader_wrn_no_content_text);
212 builder.setCancelable(false);
213 builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
214 @Override
215 public void onClick(DialogInterface dialog, int which) {
216 finish();
217 }
218 });
219 return builder.create();
220 default:
221 throw new IllegalArgumentException("Unknown dialog id: " + id);
222 }
223 }
224
225 class a implements OnClickListener {
226 String mPath;
227 EditText mDirname;
228
229 public a(String path, EditText dirname) {
230 mPath = path;
231 mDirname = dirname;
232 }
233
234 @Override
235 public void onClick(DialogInterface dialog, int which) {
236 Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
237 Uploader.this.mCreateDir = true;
238 uploadFiles();
239 }
240 }
241
242 @Override
243 public void onBackPressed() {
244
245 if (mParents.size() <= 1) {
246 super.onBackPressed();
247 return;
248 } else {
249 mParents.pop();
250 populateDirectoryList();
251 }
252 }
253
254 @Override
255 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
256 // click on folder in the list
257 Log.d(TAG, "on item click");
258 Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
259 if (tmpfiles.size() <= 0) return;
260 // filter on dirtype
261 Vector<OCFile> files = new Vector<OCFile>();
262 for (OCFile f : tmpfiles)
263 if (f.isDirectory())
264 files.add(f);
265 if (files.size() < position) {
266 throw new IndexOutOfBoundsException("Incorrect item selected");
267 }
268 mParents.push(files.get(position).getFileName());
269 populateDirectoryList();
270 }
271
272 @Override
273 public void onClick(View v) {
274 // click on button
275 switch (v.getId()) {
276 case R.id.uploader_choose_folder:
277 mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix
278 for (String p : mParents)
279 mUploadPath += p + OCFile.PATH_SEPARATOR;
280 Log.d(TAG, "Uploading file to dir " + mUploadPath);
281
282 uploadFiles();
283
284 break;
285 /*case android.R.id.button1: // dynamic action for create aditional dir
286 showDialog(DIALOG_GET_DIRNAME);
287 break;*/
288 default:
289 throw new IllegalArgumentException("Wrong element clicked");
290 }
291 }
292
293 @Override
294 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
295 super.onActivityResult(requestCode, resultCode, data);
296 Log.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
297 if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
298 dismissDialog(DIALOG_NO_ACCOUNT);
299 if (resultCode == RESULT_CANCELED) {
300 finish();
301 }
302 Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.AUTH_TOKEN_TYPE);
303 if (accounts.length == 0) {
304 showDialog(DIALOG_NO_ACCOUNT);
305 } else {
306 // there is no need for checking for is there more then one
307 // account at this point
308 // since account setup can set only one account at time
309 mAccount = accounts[0];
310 populateDirectoryList();
311 }
312 }
313 }
314
315 private void populateDirectoryList() {
316 setContentView(R.layout.uploader_layout);
317
318 String full_path = "";
319 for (String a : mParents)
320 full_path += a + "/";
321
322 Log.d(TAG, "Populating view with content of : " + full_path);
323
324 mFile = mStorageManager.getFileByPath(full_path);
325 if (mFile != null) {
326 Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);
327 List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
328 for (OCFile f : files) {
329 HashMap<String, Object> h = new HashMap<String, Object>();
330 if (f.isDirectory()) {
331 h.put("dirname", f.getFileName());
332 data.add(h);
333 }
334 }
335 SimpleAdapter sa = new SimpleAdapter(this,
336 data,
337 R.layout.uploader_list_item_layout,
338 new String[] {"dirname"},
339 new int[] {R.id.textView1});
340 setListAdapter(sa);
341 Button btn = (Button) findViewById(R.id.uploader_choose_folder);
342 btn.setOnClickListener(this);
343 getListView().setOnItemClickListener(this);
344 }
345 }
346
347 private boolean prepareStreamsToUpload() {
348 if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
349 mStreamsToUpload = new ArrayList<Parcelable>();
350 mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
351 } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
352 mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
353 }
354 return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
355 }
356
357 public void uploadFiles() {
358 try {
359 /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed
360 WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
361 // create last directory in path if necessary
362 if (mCreateDir) {
363 wdc.createDirectory(mUploadPath);
364 }
365 */
366
367 String[] local = new String[mStreamsToUpload.size()], remote = new String[mStreamsToUpload.size()];
368
369 for (int i = 0; i < mStreamsToUpload.size(); ++i) {
370 Uri uri = (Uri) mStreamsToUpload.get(i);
371 if (uri.getScheme().equals("content")) {
372 Cursor c = getContentResolver().query((Uri) mStreamsToUpload.get(i),
373 CONTENT_PROJECTION,
374 null,
375 null,
376 null);
377
378 if (!c.moveToFirst())
379 continue;
380
381 final String display_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME)),
382 data = c.getString(c.getColumnIndex(Media.DATA));
383 local[i] = data;
384 remote[i] = mUploadPath + display_name;
385 } else if (uri.getScheme().equals("file")) {
386 final File file = new File(Uri.decode(uri.toString()).replace(uri.getScheme() + "://", ""));
387 local[i] = file.getAbsolutePath();
388 remote[i] = mUploadPath + file.getName();
389 }
390
391 }
392 Intent intent = new Intent(getApplicationContext(), FileUploader.class);
393 intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
394 intent.putExtra(FileUploader.KEY_LOCAL_FILE, local);
395 intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote);
396 intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
397 startService(intent);
398 finish();
399
400 } catch (SecurityException e) {
401 String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));
402 Toast.makeText(this, message, Toast.LENGTH_LONG).show();
403 }
404 }
405
406 }