Fixed merge
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / 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 version 2,
7 * as published by the Free Software Foundation.
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
19 package com.owncloud.android.ui.activity;
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.MainApp;
30 import com.owncloud.android.R;
31 import com.owncloud.android.authentication.AccountAuthenticator;
32 import com.owncloud.android.datamodel.FileDataStorageManager;
33 import com.owncloud.android.datamodel.OCFile;
34 import com.owncloud.android.files.services.FileUploader;
35 import com.owncloud.android.utils.Log_OC;
36
37 import android.accounts.Account;
38 import android.accounts.AccountManager;
39 import android.app.AlertDialog;
40 import android.app.AlertDialog.Builder;
41 import android.app.Dialog;
42 import android.app.ListActivity;
43 import android.app.ProgressDialog;
44 import android.content.Context;
45 import android.content.DialogInterface;
46 import android.content.DialogInterface.OnCancelListener;
47 import android.content.DialogInterface.OnClickListener;
48 import android.content.Intent;
49 import android.content.SharedPreferences;
50 import android.database.Cursor;
51 import android.net.Uri;
52 import android.os.Bundle;
53 import android.os.Parcelable;
54 import android.preference.PreferenceManager;
55 import android.provider.MediaStore.Audio;
56 import android.provider.MediaStore.Images;
57 import android.provider.MediaStore.Video;
58 import android.view.View;
59 import android.view.Window;
60 import android.widget.AdapterView;
61 import android.widget.AdapterView.OnItemClickListener;
62 import android.widget.Button;
63 import android.widget.EditText;
64 import android.widget.SimpleAdapter;
65 import android.widget.Toast;
66
67 import com.actionbarsherlock.app.ActionBar;
68 import com.actionbarsherlock.app.SherlockListActivity;
69 import com.owncloud.android.MainApp;
70 import com.owncloud.android.R;
71 import com.owncloud.android.authentication.AccountAuthenticator;
72 import com.owncloud.android.datamodel.FileDataStorageManager;
73 import com.owncloud.android.datamodel.OCFile;
74 import com.owncloud.android.files.services.FileUploader;
75 import com.owncloud.android.utils.DisplayUtils;
76 import com.owncloud.android.utils.Log_OC;
77
78 import java.io.File;
79 import java.util.ArrayList;
80 import java.util.HashMap;
81 import java.util.LinkedList;
82 import java.util.List;
83 import java.util.Stack;
84 import java.util.Vector;
85
86
87 /**
88 * This can be used to upload things to an ownCloud instance.
89 *
90 * @author Bartek Przybylski
91 *
92 */
93 public class Uploader extends SherlockListActivity implements OnItemClickListener, android.view.View.OnClickListener {
94 private static final String TAG = "ownCloudUploader";
95
96 private Account mAccount;
97 private AccountManager mAccountManager;
98 private Stack<String> mParents;
99 private ArrayList<Parcelable> mStreamsToUpload;
100 private boolean mCreateDir;
101 private String mUploadPath;
102 private FileDataStorageManager mStorageManager;
103 private OCFile mFile;
104 private boolean mSaveUploadLocation;
105
106 private final static int DIALOG_NO_ACCOUNT = 0;
107 private final static int DIALOG_WAITING = 1;
108 private final static int DIALOG_NO_STREAM = 2;
109 private final static int DIALOG_MULTIPLE_ACCOUNT = 3;
110
111 private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
112
113 @Override
114 protected void onCreate(Bundle savedInstanceState) {
115 super.onCreate(savedInstanceState);
116 mParents = new Stack<String>();
117
118 ActionBar actionBar = getSherlock().getActionBar();
119 actionBar.setIcon(DisplayUtils.getSeasonalIconId());
120
121 SharedPreferences appPreferences = PreferenceManager
122 .getDefaultSharedPreferences(getApplicationContext());
123
124 mSaveUploadLocation = appPreferences.getBoolean("save_last_upload_location", false);
125
126 if(mSaveUploadLocation)
127 {
128 String last_path = appPreferences.getString("last_upload_path", "");
129
130 if(last_path.equals("/")) {
131 mParents.add("");
132 }
133 else{
134 String[] dir_names = last_path.split("/");
135 for (String dir : dir_names)
136 mParents.add(dir);
137 }
138 }
139 else {
140 mParents.add("");
141 }
142
143 if (prepareStreamsToUpload()) {
144 mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
145 Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
146 if (accounts.length == 0) {
147 Log_OC.i(TAG, "No ownCloud account is available");
148 showDialog(DIALOG_NO_ACCOUNT);
149 } else if (accounts.length > 1) {
150 Log_OC.i(TAG, "More then one ownCloud is available");
151 showDialog(DIALOG_MULTIPLE_ACCOUNT);
152 } else {
153 mAccount = accounts[0];
154 mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
155 populateDirectoryList();
156 }
157 } else {
158 showDialog(DIALOG_NO_STREAM);
159 }
160 }
161
162 @Override
163 protected Dialog onCreateDialog(final int id) {
164 final AlertDialog.Builder builder = new Builder(this);
165 switch (id) {
166 case DIALOG_WAITING:
167 ProgressDialog pDialog = new ProgressDialog(this);
168 pDialog.setIndeterminate(false);
169 pDialog.setCancelable(false);
170 pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading));
171 return pDialog;
172 case DIALOG_NO_ACCOUNT:
173 builder.setIcon(android.R.drawable.ic_dialog_alert);
174 builder.setTitle(R.string.uploader_wrn_no_account_title);
175 builder.setMessage(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
176 builder.setCancelable(false);
177 builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
178 @Override
179 public void onClick(DialogInterface dialog, int which) {
180 if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
181 // using string value since in API7 this
182 // constatn is not defined
183 // in API7 < this constatant is defined in
184 // Settings.ADD_ACCOUNT_SETTINGS
185 // and Settings.EXTRA_AUTHORITIES
186 Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);
187 intent.putExtra("authorities", new String[] { MainApp.getAuthTokenType() });
188 startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
189 } else {
190 // since in API7 there is no direct call for
191 // account setup, so we need to
192 // show our own AccountSetupAcricity, get
193 // desired results and setup
194 // everything for ourself
195 Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
196 startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
197 }
198 }
199 });
200 builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {
201 @Override
202 public void onClick(DialogInterface dialog, int which) {
203 finish();
204 }
205 });
206 return builder.create();
207 case DIALOG_MULTIPLE_ACCOUNT:
208 CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
209 for (int i = 0; i < ac.length; ++i) {
210 ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name;
211 }
212 builder.setTitle(R.string.common_choose_account);
213 builder.setItems(ac, new OnClickListener() {
214 @Override
215 public void onClick(DialogInterface dialog, int which) {
216 mAccount = mAccountManager.getAccountsByType(MainApp.getAccountType())[which];
217 mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
218 populateDirectoryList();
219 }
220 });
221 builder.setCancelable(true);
222 builder.setOnCancelListener(new OnCancelListener() {
223 @Override
224 public void onCancel(DialogInterface dialog) {
225 dialog.cancel();
226 finish();
227 }
228 });
229 return builder.create();
230 case DIALOG_NO_STREAM:
231 builder.setIcon(android.R.drawable.ic_dialog_alert);
232 builder.setTitle(R.string.uploader_wrn_no_content_title);
233 builder.setMessage(R.string.uploader_wrn_no_content_text);
234 builder.setCancelable(false);
235 builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
236 @Override
237 public void onClick(DialogInterface dialog, int which) {
238 finish();
239 }
240 });
241 return builder.create();
242 default:
243 throw new IllegalArgumentException("Unknown dialog id: " + id);
244 }
245 }
246
247 class a implements OnClickListener {
248 String mPath;
249 EditText mDirname;
250
251 public a(String path, EditText dirname) {
252 mPath = path;
253 mDirname = dirname;
254 }
255
256 @Override
257 public void onClick(DialogInterface dialog, int which) {
258 Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
259 Uploader.this.mCreateDir = true;
260 uploadFiles();
261 }
262 }
263
264 @Override
265 public void onBackPressed() {
266
267 if (mParents.size() <= 1) {
268 super.onBackPressed();
269 return;
270 } else {
271 mParents.pop();
272 populateDirectoryList();
273 }
274 }
275
276 @Override
277 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
278 // click on folder in the list
279 Log_OC.d(TAG, "on item click");
280 Vector<OCFile> tmpfiles = mStorageManager.getFolderContent(mFile);
281 if (tmpfiles.size() <= 0) return;
282 // filter on dirtype
283 Vector<OCFile> files = new Vector<OCFile>();
284 for (OCFile f : tmpfiles)
285 if (f.isFolder())
286 files.add(f);
287 if (files.size() < position) {
288 throw new IndexOutOfBoundsException("Incorrect item selected");
289 }
290 mParents.push(files.get(position).getFileName());
291 populateDirectoryList();
292 }
293
294 @Override
295 public void onClick(View v) {
296 // click on button
297 switch (v.getId()) {
298 case R.id.uploader_choose_folder:
299 mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix
300 for (String p : mParents)
301 mUploadPath += p + OCFile.PATH_SEPARATOR;
302 Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
303
304 uploadFiles();
305
306 break;
307 default:
308 throw new IllegalArgumentException("Wrong element clicked");
309 }
310 }
311
312 @Override
313 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
314 super.onActivityResult(requestCode, resultCode, data);
315 Log_OC.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
316 if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
317 dismissDialog(DIALOG_NO_ACCOUNT);
318 if (resultCode == RESULT_CANCELED) {
319 finish();
320 }
321 Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAuthTokenType());
322 if (accounts.length == 0) {
323 showDialog(DIALOG_NO_ACCOUNT);
324 } else {
325 // there is no need for checking for is there more then one
326 // account at this point
327 // since account setup can set only one account at time
328 mAccount = accounts[0];
329 populateDirectoryList();
330 }
331 }
332 }
333
334 private void populateDirectoryList() {
335 setContentView(R.layout.uploader_layout);
336
337 String current_dir = mParents.peek();
338 if(current_dir.equals("")){
339 getActionBar().setTitle(getString(R.string.default_display_name_for_root_folder));
340 }
341 else{
342 getActionBar().setTitle(current_dir);
343 }
344
345 String full_path = "";
346
347 for (String a : mParents)
348 full_path += a + "/";
349
350 Log_OC.d(TAG, "Populating view with content of : " + full_path);
351
352 mFile = mStorageManager.getFileByPath(full_path);
353 if (mFile != null) {
354 Vector<OCFile> files = mStorageManager.getFolderContent(mFile);
355 List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
356 for (OCFile f : files) {
357 HashMap<String, Object> h = new HashMap<String, Object>();
358 if (f.isFolder()) {
359 h.put("dirname", f.getFileName());
360 data.add(h);
361 }
362 }
363 SimpleAdapter sa = new SimpleAdapter(this,
364 data,
365 R.layout.uploader_list_item_layout,
366 new String[] {"dirname"},
367 new int[] {R.id.textView1});
368 setListAdapter(sa);
369 Button btn = (Button) findViewById(R.id.uploader_choose_folder);
370 btn.setOnClickListener(this);
371 getListView().setOnItemClickListener(this);
372 }
373 }
374
375 private boolean prepareStreamsToUpload() {
376 if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
377 mStreamsToUpload = new ArrayList<Parcelable>();
378 mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
379 } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
380 mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
381 }
382 return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
383 }
384
385 public void uploadFiles() {
386 try {
387
388 ArrayList<String> local = new ArrayList<String>();
389 ArrayList<String> remote = new ArrayList<String>();
390
391 // this checks the mimeType
392 for (Parcelable mStream : mStreamsToUpload) {
393
394 Uri uri = (Uri) mStream;
395 if (uri !=null) {
396 if (uri.getScheme().equals("content")) {
397
398 String mimeType = getContentResolver().getType(uri);
399
400 if (mimeType.contains("image")) {
401 String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
402 Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
403 c.moveToFirst();
404 int index = c.getColumnIndex(Images.Media.DATA);
405 String data = c.getString(index);
406 local.add(data);
407 remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
408
409 }
410 else if (mimeType.contains("video")) {
411 String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED };
412 Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
413 c.moveToFirst();
414 int index = c.getColumnIndex(Video.Media.DATA);
415 String data = c.getString(index);
416 local.add(data);
417 remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
418
419 }
420 else if (mimeType.contains("audio")) {
421 String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE };
422 Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
423 c.moveToFirst();
424 int index = c.getColumnIndex(Audio.Media.DATA);
425 String data = c.getString(index);
426 local.add(data);
427 remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
428
429 }
430 else {
431 String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
432 // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI
433 if (filePath.contains("mnt")) {
434 String splitedFilePath[] = filePath.split("/mnt");
435 filePath = splitedFilePath[1];
436 }
437 final File file = new File(filePath);
438 local.add(file.getAbsolutePath());
439 remote.add(mUploadPath + file.getName());
440 }
441
442 } else if (uri.getScheme().equals("file")) {
443 String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
444 if (filePath.contains("mnt")) {
445 String splitedFilePath[] = filePath.split("/mnt");
446 filePath = splitedFilePath[1];
447 }
448 final File file = new File(filePath);
449 local.add(file.getAbsolutePath());
450 remote.add(mUploadPath + file.getName());
451 }
452 else {
453 throw new SecurityException();
454 }
455 }
456 else {
457 throw new SecurityException();
458 }
459
460 Intent intent = new Intent(getApplicationContext(), FileUploader.class);
461 intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
462 intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
463 intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()]));
464 intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
465 startService(intent);
466
467 if(mSaveUploadLocation){
468 SharedPreferences.Editor appPrefs = PreferenceManager
469 .getDefaultSharedPreferences(getApplicationContext()).edit();
470 appPrefs.putString("last_upload_path", mUploadPath);
471 appPrefs.commit();
472 }
473
474 finish();
475 }
476
477 } catch (SecurityException e) {
478 String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));
479 Toast.makeText(this, message, Toast.LENGTH_LONG).show();
480 }
481 }
482
483 }