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