3310ff46d3c12e63c813ac4b8d718c1e979b8783
[pub/Android/ownCloud.git] / src / eu / alefzero / owncloud / ui / activity / FileDisplayActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2011 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
19 package eu.alefzero.owncloud.ui.activity;
20
21 import java.io.File;
22 import java.net.URLDecoder;
23 import java.net.URLEncoder;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26
27 import android.accounts.Account;
28 import android.accounts.AccountManager;
29 import android.app.AlertDialog;
30 import android.app.AlertDialog.Builder;
31 import android.app.Dialog;
32 import android.content.BroadcastReceiver;
33 import android.content.ContentResolver;
34 import android.content.Context;
35 import android.content.DialogInterface;
36 import android.content.DialogInterface.OnClickListener;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.database.Cursor;
40 import android.net.Uri;
41 import android.os.Bundle;
42 import android.provider.MediaStore;
43 import android.util.Log;
44 import android.view.View;
45 import android.view.ViewGroup;
46 import android.widget.ArrayAdapter;
47 import android.widget.EditText;
48 import android.widget.TextView;
49
50 import com.actionbarsherlock.app.ActionBar;
51 import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
52 import com.actionbarsherlock.app.SherlockFragmentActivity;
53 import com.actionbarsherlock.view.Menu;
54 import com.actionbarsherlock.view.MenuInflater;
55 import com.actionbarsherlock.view.MenuItem;
56 import com.actionbarsherlock.view.Window;
57
58 import eu.alefzero.owncloud.AccountUtils;
59 import eu.alefzero.owncloud.R;
60 import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
61 import eu.alefzero.owncloud.datamodel.DataStorageManager;
62 import eu.alefzero.owncloud.datamodel.FileDataStorageManager;
63 import eu.alefzero.owncloud.datamodel.OCFile;
64 import eu.alefzero.owncloud.files.services.FileUploader;
65 import eu.alefzero.owncloud.syncadapter.FileSyncService;
66 import eu.alefzero.owncloud.ui.fragment.FileDetailFragment;
67 import eu.alefzero.owncloud.ui.fragment.FileListFragment;
68 import eu.alefzero.webdav.WebdavClient;
69
70 /**
71 * Displays, what files the user has available in his ownCloud.
72 *
73 * @author Bartek Przybylski
74 *
75 */
76
77 public class FileDisplayActivity extends SherlockFragmentActivity implements
78 OnNavigationListener, OnClickListener {
79 private ArrayAdapter<String> mDirectories;
80 private DataStorageManager mStorageManager;
81 private FileListFragment mFileList;
82 private OCFile mCurrentDir;
83 private String[] mDirs = null;
84
85 private SyncBroadcastReceiver syncBroadcastRevceiver;
86
87 private static final String KEY_DIR_ARRAY = "DIR_ARRAY";
88 private static final String KEY_CURRENT_DIR = "DIR";
89
90 private static final int DIALOG_SETUP_ACCOUNT = 0;
91 private static final int DIALOG_CREATE_DIR = 1;
92 private static final int ACTION_SELECT_FILE = 1;
93
94 @Override
95 public void onCreate(Bundle savedInstanceState) {
96 super.onCreate(savedInstanceState);
97
98 if (!accountsAreSetup()) {
99 showDialog(DIALOG_SETUP_ACCOUNT);
100 return;
101 }
102
103 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
104 setProgressBarIndeterminateVisibility(false);
105
106 if(savedInstanceState != null){
107 mCurrentDir = (OCFile) savedInstanceState.getParcelable(KEY_CURRENT_DIR);
108 }
109
110 setContentView(R.layout.files);
111 }
112
113 @Override
114 public boolean onCreateOptionsMenu(Menu menu) {
115 MenuInflater inflater = getSherlock().getMenuInflater();
116 inflater.inflate(R.menu.menu, menu);
117 return true;
118 }
119
120 @Override
121 public boolean onOptionsItemSelected(MenuItem item) {
122 boolean retval = true;
123 switch (item.getItemId()) {
124 case R.id.createDirectoryItem: {
125 showDialog(DIALOG_CREATE_DIR);
126 break;
127 }
128 case R.id.startSync: {
129 Bundle bundle = new Bundle();
130 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
131 ContentResolver.requestSync(
132 AccountUtils.getCurrentOwnCloudAccount(this),
133 "org.owncloud", bundle);
134 break;
135 }
136 case R.id.action_upload: {
137 Intent action = new Intent(Intent.ACTION_GET_CONTENT);
138 action = action.setType("*/*")
139 .addCategory(Intent.CATEGORY_OPENABLE);
140 startActivityForResult(
141 Intent.createChooser(action, "Upload file from..."),
142 ACTION_SELECT_FILE);
143 break;
144 }
145
146 case android.R.id.home: {
147 Intent i = new Intent(this, AccountSelectActivity.class);
148 startActivity(i);
149 finish();
150 break;
151 }
152 default:
153 retval = false;
154 }
155 return retval;
156 }
157
158 @Override
159 public boolean onNavigationItemSelected(int itemPosition, long itemId) {
160 int i = itemPosition;
161 while (i-- != 0) {
162 onBackPressed();
163 }
164 return true;
165 }
166
167 /**
168 * Called, when the user selected something for uploading
169 */
170 public void onActivityResult(int requestCode, int resultCode, Intent data) {
171 if (resultCode == RESULT_OK) {
172 if (requestCode == ACTION_SELECT_FILE) {
173 Uri selectedImageUri = data.getData();
174
175 String filemanagerstring = selectedImageUri.getPath();
176 String selectedImagePath = getPath(selectedImageUri);
177 String filepath;
178
179 if (selectedImagePath != null)
180 filepath = selectedImagePath;
181 else
182 filepath = filemanagerstring;
183
184 if (filepath == null) {
185 Log.e("FileDisplay", "Couldnt resolve path to file");
186 return;
187 }
188
189 Intent i = new Intent(this, FileUploader.class);
190 i.putExtra(FileUploader.KEY_ACCOUNT,
191 AccountUtils.getCurrentOwnCloudAccount(this));
192 String remotepath = new String();
193 for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
194 remotepath += "/" + URLEncoder.encode(mDirectories.getItem(j));
195 }
196 if (!remotepath.endsWith("/"))
197 remotepath += "/";
198 remotepath += URLEncoder.encode(new File(filepath).getName());
199 Log.e("ASD", remotepath + "");
200
201 i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
202 i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
203 i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
204 startService(i);
205 }
206 }
207 }
208
209 @Override
210 public void onBackPressed() {
211 if (mDirectories.getCount() == 1) {
212 finish();
213 return;
214 }
215 popDirname();
216 mFileList.onNavigateUp();
217 }
218
219 @Override
220 protected void onRestoreInstanceState(Bundle savedInstanceState) {
221 super.onRestoreInstanceState(savedInstanceState);
222 // Check, if there are ownCloud accounts
223 if (!accountsAreSetup()) {
224 showDialog(DIALOG_SETUP_ACCOUNT);
225 }
226 mDirs = savedInstanceState.getStringArray(KEY_DIR_ARRAY);
227 mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
228 mDirectories.add("/");
229 if (mDirs != null)
230 for (String s : mDirs)
231 mDirectories.insert(s, 0);
232 }
233
234 @Override
235 protected void onSaveInstanceState(Bundle outState) {
236 super.onSaveInstanceState(outState);
237 if(mDirectories != null){
238 mDirs = new String[mDirectories.getCount()-1];
239 for (int j = mDirectories.getCount() - 2, i = 0; j >= 0; --j, ++i) {
240 mDirs[i] = mDirectories.getItem(j);
241 }
242 }
243 outState.putStringArray(KEY_DIR_ARRAY, mDirs);
244 outState.putParcelable(KEY_CURRENT_DIR, mCurrentDir);
245 }
246
247 @Override
248 protected void onResume() {
249 super.onResume();
250
251 //TODO: Dialog useless -> get rid of this
252 if (!accountsAreSetup()) {
253 showDialog(DIALOG_SETUP_ACCOUNT);
254 return;
255 }
256
257 // Listen for sync messages
258 IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.SYNC_MESSAGE);
259 syncBroadcastRevceiver = new SyncBroadcastReceiver();
260 registerReceiver(syncBroadcastRevceiver, syncIntentFilter);
261
262 // Storage manager initialization
263 mStorageManager = new FileDataStorageManager(
264 AccountUtils.getCurrentOwnCloudAccount(this),
265 getContentResolver());
266
267 // File list
268 mFileList = (FileListFragment) getSupportFragmentManager().findFragmentById(R.id.fileList);
269
270 // Figure out what directory to list.
271 // Priority: Intent (here), savedInstanceState (onCreate), root dir (dir is null)
272 if(getIntent().hasExtra(FileDetailFragment.EXTRA_FILE)){
273 mCurrentDir = (OCFile) getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);
274 if(!mCurrentDir.isDirectory()){
275 mCurrentDir = mStorageManager.getFileById(mCurrentDir.getParentId());
276 }
277 }
278
279 // Drop-Down navigation and file list restore
280 mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
281
282
283 // Given the case we have a file to display:
284 if(mCurrentDir != null){
285 ArrayList<OCFile> files = new ArrayList<OCFile>();
286 OCFile currFile = mCurrentDir;
287 while(currFile != null){
288 files.add(currFile);
289 currFile = mStorageManager.getFileById(currFile.getParentId());
290 }
291
292 // Insert in mDirs
293 mDirs = new String[files.size()];
294 for(int i = files.size() - 1; i >= 0; i--){
295 mDirs[i] = files.get(i).getFileName();
296 }
297 }
298
299 if (mDirs != null) {
300 for (String s : mDirs)
301 mDirectories.add(s);
302 } else {
303 mDirectories.add("/");
304 }
305
306 // Actionbar setup
307 ActionBar action_bar = getSupportActionBar();
308 action_bar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
309 action_bar.setDisplayShowTitleEnabled(false);
310 action_bar.setListNavigationCallbacks(mDirectories, this);
311 action_bar.setDisplayHomeAsUpEnabled(true);
312
313 // List dir here
314 mFileList.listDirectory(mCurrentDir);
315 }
316
317 @Override
318 protected void onPause() {
319 super.onPause();
320 if (syncBroadcastRevceiver != null) {
321 unregisterReceiver(syncBroadcastRevceiver);
322 syncBroadcastRevceiver = null;
323 }
324
325 }
326
327 @Override
328 protected Dialog onCreateDialog(int id) {
329 Dialog dialog;
330 AlertDialog.Builder builder;
331 switch (id) {
332 case DIALOG_SETUP_ACCOUNT:
333 builder = new AlertDialog.Builder(this);
334 builder.setTitle(R.string.main_tit_accsetup);
335 builder.setMessage(R.string.main_wrn_accsetup);
336 builder.setCancelable(false);
337 builder.setPositiveButton(android.R.string.ok, this);
338 builder.setNegativeButton(android.R.string.cancel, this);
339 dialog = builder.create();
340 break;
341 case DIALOG_CREATE_DIR: {
342 builder = new Builder(this);
343 final EditText dirNameInput = new EditText(getBaseContext());
344 final Account a = AccountUtils.getCurrentOwnCloudAccount(this);
345 builder.setView(dirNameInput);
346 builder.setTitle(R.string.uploader_info_dirname);
347 int typed_color = getResources().getColor(R.color.setup_text_typed);
348 dirNameInput.setTextColor(typed_color);
349
350 builder.setPositiveButton(android.R.string.ok,
351 new OnClickListener() {
352 public void onClick(DialogInterface dialog, int which) {
353 String directoryName = dirNameInput.getText().toString();
354 if (directoryName.trim().length() == 0) {
355 dialog.cancel();
356 return;
357 }
358
359 // Figure out the path where the dir needs to be created
360 String path = mCurrentDir.getRemotePath();
361
362 // Create directory
363 path += directoryName + "/";
364 Thread thread = new Thread(new DirectoryCreator(
365 path, a));
366 thread.start();
367
368 // Save new directory in local database
369 OCFile newDir = new OCFile(path);
370 newDir.setMimetype("DIR");
371 newDir.setParentId(mCurrentDir.getFileId());
372 mStorageManager.saveFile(newDir);
373
374 // Display the new folder right away
375 dialog.dismiss();
376 mFileList.listDirectory(mCurrentDir);
377 }
378 });
379 builder.setNegativeButton(R.string.common_cancel,
380 new OnClickListener() {
381 public void onClick(DialogInterface dialog, int which) {
382 dialog.cancel();
383 }
384 });
385 dialog = builder.create();
386 break;
387 }
388 default:
389 dialog = null;
390 }
391
392 return dialog;
393 }
394
395
396 /**
397 * Responds to the "There are no ownCloud Accounts setup" dialog
398 * TODO: Dialog is 100% useless -> Remove
399 */
400 @Override
401 public void onClick(DialogInterface dialog, int which) {
402 // In any case - we won't need it anymore
403 dialog.dismiss();
404 switch (which) {
405 case DialogInterface.BUTTON_POSITIVE:
406 Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
407 intent.putExtra("authorities",
408 new String[] { AccountAuthenticator.AUTH_TOKEN_TYPE });
409 startActivity(intent);
410 break;
411 case DialogInterface.BUTTON_NEGATIVE:
412 finish();
413 }
414
415 }
416
417 /**
418 * Translates a content URI of an image to a physical path
419 * on the disk
420 * @param uri The URI to resolve
421 * @return The path to the image or null if it could not be found
422 */
423 public String getPath(Uri uri) {
424 String[] projection = { MediaStore.Images.Media.DATA };
425 Cursor cursor = managedQuery(uri, projection, null, null, null);
426 if (cursor != null) {
427 int column_index = cursor
428 .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
429 cursor.moveToFirst();
430 return cursor.getString(column_index);
431 }
432 return null;
433 }
434
435 /**
436 * Pushes a directory to the drop down list
437 * @param directory to push
438 * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
439 */
440 public void pushDirname(OCFile directory) {
441 if(!directory.isDirectory()){
442 throw new IllegalArgumentException("Only directories may be pushed!");
443 }
444 mDirectories.insert(directory.getFileName(), 0);
445 mCurrentDir = directory;
446 }
447
448 /**
449 * Pops a directory name from the drop down list
450 * @return True, unless the stack is empty
451 */
452 public boolean popDirname() {
453 mDirectories.remove(mDirectories.getItem(0));
454 return !mDirectories.isEmpty();
455 }
456
457 /**
458 * Checks, whether or not there are any ownCloud accounts setup.
459 *
460 * @return true, if there is at least one account.
461 */
462 private boolean accountsAreSetup() {
463 AccountManager accMan = AccountManager.get(this);
464 Account[] accounts = accMan
465 .getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
466 return accounts.length > 0;
467 }
468
469 private class DirectoryCreator implements Runnable {
470 private String mTargetPath;
471 private Account mAccount;
472 private AccountManager mAm;
473
474 public DirectoryCreator(String targetPath, Account account) {
475 mTargetPath = targetPath;
476 mAccount = account;
477 mAm = (AccountManager) getSystemService(ACCOUNT_SERVICE);
478 }
479
480 @Override
481 public void run() {
482 WebdavClient wdc = new WebdavClient(Uri.parse(mAm.getUserData(
483 mAccount, AccountAuthenticator.KEY_OC_URL)));
484
485 String username = mAccount.name.substring(0,
486 mAccount.name.lastIndexOf('@'));
487 String password = mAm.getPassword(mAccount);
488
489 wdc.setCredentials(username, password);
490 wdc.allowUnsignedCertificates();
491 wdc.createDirectory(mTargetPath);
492 }
493
494 }
495
496 // Custom array adapter to override text colors
497 private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
498
499 public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
500 super(ctx, view);
501 }
502
503 public View getView(int position, View convertView, ViewGroup parent) {
504 View v = super.getView(position, convertView, parent);
505
506 ((TextView) v).setTextColor(getResources().getColorStateList(
507 android.R.color.white));
508 return v;
509 }
510
511 public View getDropDownView(int position, View convertView,
512 ViewGroup parent) {
513 View v = super.getDropDownView(position, convertView, parent);
514
515 ((TextView) v).setTextColor(getResources().getColorStateList(
516 android.R.color.white));
517
518 return v;
519 }
520
521 }
522
523 private class SyncBroadcastReceiver extends BroadcastReceiver {
524 /**
525 * {@link BroadcastReceiver} to enable syncing feedback in UI
526 */
527 @Override
528 public void onReceive(Context context, Intent intent) {
529 boolean inProgress = intent.getBooleanExtra(
530 FileSyncService.IN_PROGRESS, false);
531 String account_name = intent
532 .getStringExtra(FileSyncService.ACCOUNT_NAME);
533 Log.d("FileDisplay", "sync of account " + account_name
534 + " is in_progress: " + inProgress);
535 setProgressBarIndeterminateVisibility(inProgress);
536 if (!inProgress) {
537 FileListFragment fileListFramgent = (FileListFragment) getSupportFragmentManager()
538 .findFragmentById(R.id.fileList);
539 if (fileListFramgent != null)
540 fileListFramgent.listDirectory();
541 }
542 }
543
544 }
545
546 }