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