aa59a5c24b7923b0b6dab71a40f56e0f8a3968e8
[pub/Android/ownCloud.git] / src / eu / alefzero / owncloud / ui / fragment / FileDetailFragment.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 package eu.alefzero.owncloud.ui.fragment;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24
25 import org.apache.commons.httpclient.HttpException;
26 import org.apache.commons.httpclient.methods.GetMethod;
27 import org.apache.commons.httpclient.methods.PostMethod;
28 import org.apache.commons.httpclient.methods.StringRequestEntity;
29 import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
30 import org.apache.http.HttpStatus;
31 import org.apache.http.NameValuePair;
32 import org.apache.http.client.utils.URLEncodedUtils;
33 import org.apache.http.message.BasicNameValuePair;
34 import org.apache.http.protocol.HTTP;
35 import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
36 import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
37 import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
38 import org.json.JSONException;
39 import org.json.JSONObject;
40
41 import android.accounts.Account;
42 import android.accounts.AccountManager;
43 import android.annotation.SuppressLint;
44 import android.app.Activity;
45 import android.content.ActivityNotFoundException;
46 import android.content.BroadcastReceiver;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.IntentFilter;
50 import android.content.res.Resources.NotFoundException;
51 import android.graphics.Bitmap;
52 import android.graphics.BitmapFactory;
53 import android.graphics.BitmapFactory.Options;
54 import android.graphics.Point;
55 import android.net.Uri;
56 import android.os.AsyncTask;
57 import android.os.Bundle;
58 import android.os.Handler;
59 import android.support.v4.app.FragmentTransaction;
60 import android.util.Log;
61 import android.view.Display;
62 import android.view.LayoutInflater;
63 import android.view.View;
64 import android.view.View.OnClickListener;
65 import android.view.ViewGroup;
66 import android.view.WindowManager.LayoutParams;
67 import android.webkit.MimeTypeMap;
68 import android.widget.Button;
69 import android.widget.CheckBox;
70 import android.widget.ImageView;
71 import android.widget.TextView;
72 import android.widget.Toast;
73
74 import com.actionbarsherlock.app.SherlockDialogFragment;
75 import com.actionbarsherlock.app.SherlockFragment;
76
77 import eu.alefzero.owncloud.AccountUtils;
78 import eu.alefzero.owncloud.DisplayUtils;
79 import eu.alefzero.owncloud.R;
80 import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
81 import eu.alefzero.owncloud.datamodel.FileDataStorageManager;
82 import eu.alefzero.owncloud.datamodel.OCFile;
83 import eu.alefzero.owncloud.files.services.FileDownloader;
84 import eu.alefzero.owncloud.files.services.FileUploader;
85 import eu.alefzero.owncloud.ui.activity.FileDisplayActivity;
86 import eu.alefzero.owncloud.utils.OwnCloudVersion;
87 import eu.alefzero.webdav.WebdavClient;
88 import eu.alefzero.webdav.WebdavUtils;
89
90 /**
91 * This Fragment is used to display the details about a file.
92 *
93 * @author Bartek Przybylski
94 *
95 */
96 public class FileDetailFragment extends SherlockFragment implements
97 OnClickListener, ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
98
99 public static final String EXTRA_FILE = "FILE";
100 public static final String EXTRA_ACCOUNT = "ACCOUNT";
101
102 private FileDetailFragment.ContainerActivity mContainerActivity;
103
104 private int mLayout;
105 private View mView;
106 private OCFile mFile;
107 private Account mAccount;
108 private ImageView mPreview;
109
110 private DownloadFinishReceiver mDownloadFinishReceiver;
111 private UploadFinishReceiver mUploadFinishReceiver;
112
113 private static final String TAG = "FileDetailFragment";
114 public static final String FTAG = "FileDetails";
115 public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
116
117
118 /**
119 * Creates an empty details fragment.
120 *
121 * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
122 */
123 public FileDetailFragment() {
124 mFile = null;
125 mAccount = null;
126 mLayout = R.layout.file_details_empty;
127 }
128
129
130 /**
131 * Creates a details fragment.
132 *
133 * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
134 *
135 * @param fileToDetail An {@link OCFile} to show in the fragment
136 * @param ocAccount An ownCloud account; needed to start downloads
137 */
138 public FileDetailFragment(OCFile fileToDetail, Account ocAccount){
139 mFile = fileToDetail;
140 mAccount = ocAccount;
141 mLayout = R.layout.file_details_empty;
142
143 if(fileToDetail != null && ocAccount != null) {
144 mLayout = R.layout.file_details_fragment;
145 }
146 }
147
148
149 /**
150 * {@inheritDoc}
151 */
152 @Override
153 public void onAttach(Activity activity) {
154 super.onAttach(activity);
155 try {
156 mContainerActivity = (ContainerActivity) activity;
157 } catch (ClassCastException e) {
158 throw new ClassCastException(activity.toString() + " must implement FileListFragment.ContainerActivity");
159 }
160 }
161
162
163 @Override
164 public View onCreateView(LayoutInflater inflater, ViewGroup container,
165 Bundle savedInstanceState) {
166 super.onCreateView(inflater, container, savedInstanceState);
167
168 if (savedInstanceState != null) {
169 mFile = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_FILE);
170 mAccount = savedInstanceState.getParcelable(FileDetailFragment.EXTRA_ACCOUNT);
171 }
172
173 View view = null;
174 view = inflater.inflate(mLayout, container, false);
175 mView = view;
176
177 if (mLayout == R.layout.file_details_fragment) {
178 mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
179 mView.findViewById(R.id.fdRenameBtn).setOnClickListener(this);
180 mView.findViewById(R.id.fdDownloadBtn).setOnClickListener(this);
181 mView.findViewById(R.id.fdOpenBtn).setOnClickListener(this);
182 mView.findViewById(R.id.fdRemoveBtn).setOnClickListener(this);
183 //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this);
184 mPreview = (ImageView)mView.findViewById(R.id.fdPreview);
185 }
186
187 updateFileDetails();
188 return view;
189 }
190
191
192 @Override
193 public void onSaveInstanceState(Bundle outState) {
194 Log.i(getClass().toString(), "onSaveInstanceState() start");
195 super.onSaveInstanceState(outState);
196 outState.putParcelable(FileDetailFragment.EXTRA_FILE, mFile);
197 outState.putParcelable(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
198 Log.i(getClass().toString(), "onSaveInstanceState() end");
199 }
200
201
202 @Override
203 public void onResume() {
204 super.onResume();
205
206 mDownloadFinishReceiver = new DownloadFinishReceiver();
207 IntentFilter filter = new IntentFilter(
208 FileDownloader.DOWNLOAD_FINISH_MESSAGE);
209 getActivity().registerReceiver(mDownloadFinishReceiver, filter);
210
211 mUploadFinishReceiver = new UploadFinishReceiver();
212 filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
213 getActivity().registerReceiver(mUploadFinishReceiver, filter);
214
215 mPreview = (ImageView)mView.findViewById(R.id.fdPreview);
216 }
217
218 @Override
219 public void onPause() {
220 super.onPause();
221
222 getActivity().unregisterReceiver(mDownloadFinishReceiver);
223 mDownloadFinishReceiver = null;
224
225 getActivity().unregisterReceiver(mUploadFinishReceiver);
226 mUploadFinishReceiver = null;
227
228 if (mPreview != null) {
229 mPreview = null;
230 }
231 }
232
233 @Override
234 public View getView() {
235 return super.getView() == null ? mView : super.getView();
236 }
237
238
239
240 @Override
241 public void onClick(View v) {
242 switch (v.getId()) {
243 case R.id.fdDownloadBtn: {
244 Intent i = new Intent(getActivity(), FileDownloader.class);
245 i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
246 i.putExtra(FileDownloader.EXTRA_REMOTE_PATH, mFile.getRemotePath());
247 i.putExtra(FileDownloader.EXTRA_FILE_PATH, mFile.getRemotePath());
248 i.putExtra(FileDownloader.EXTRA_FILE_SIZE, mFile.getFileLength());
249
250 // update ui
251 setButtonsForTransferring();
252
253 getActivity().startService(i);
254 mContainerActivity.onFileStateChanged(); // this is not working; it is performed before the fileDownloadService registers it as 'in progress'
255 break;
256 }
257 case R.id.fdKeepInSync: {
258 CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
259 mFile.setKeepInSync(cb.isChecked());
260 FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
261 fdsm.saveFile(mFile);
262 if (mFile.keepInSync()) {
263 onClick(getView().findViewById(R.id.fdDownloadBtn));
264 } else {
265 mContainerActivity.onFileStateChanged(); // put inside 'else' to not call it twice (here, and in the virtual click on fdDownloadBtn)
266 }
267 break;
268 }
269 case R.id.fdRenameBtn: {
270 EditNameFragment dialog = EditNameFragment.newInstance(mFile.getFileName());
271 dialog.show(getFragmentManager(), "nameeditdialog");
272 dialog.setOnDismissListener(this);
273 break;
274 }
275 case R.id.fdRemoveBtn: {
276 ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(R.string.confirmation_remove_alert, new String[]{mFile.getFileName()});
277 confDialog.setOnConfirmationListener(this);
278 confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
279 break;
280 }
281 case R.id.fdOpenBtn: {
282 String storagePath = mFile.getStoragePath();
283 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
284 try {
285 Intent i = new Intent(Intent.ACTION_VIEW);
286 i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());
287 i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
288 startActivity(i);
289
290 } catch (Throwable t) {
291 Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
292 boolean toastIt = true;
293 String mimeType = "";
294 try {
295 Intent i = new Intent(Intent.ACTION_VIEW);
296 mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
297 if (mimeType != null && !mimeType.equals(mFile.getMimetype())) {
298 i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
299 i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
300 startActivity(i);
301 toastIt = false;
302 }
303
304 } catch (IndexOutOfBoundsException e) {
305 Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
306
307 } catch (ActivityNotFoundException e) {
308 Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
309
310 } catch (Throwable th) {
311 Log.e(TAG, "Unexpected problem when opening: " + storagePath, th);
312
313 } finally {
314 if (toastIt) {
315 Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
316 }
317 }
318
319 }
320 break;
321 }
322 default:
323 Log.e(TAG, "Incorrect view clicked!");
324 }
325
326 /* else if (v.getId() == R.id.fdShareBtn) {
327 Thread t = new Thread(new ShareRunnable(mFile.getRemotePath()));
328 t.start();
329 }*/
330 }
331
332
333 @Override
334 public void onConfirmation(boolean confirmation, String callerTag) {
335 if (confirmation && callerTag.equals(FTAG_CONFIRMATION)) {
336 Log.e("ASD","onConfirmation");
337 FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getContentResolver());
338 if (fdsm.getFileById(mFile.getFileId()) != null) {
339 new Thread(new RemoveRunnable(mFile, mAccount, new Handler())).start();
340 }
341 } else if (!confirmation) Log.d(TAG, "REMOVAL CANCELED");
342 }
343
344
345 /**
346 * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
347 *
348 * @return True when the fragment was created with the empty layout.
349 */
350 public boolean isEmpty() {
351 return mLayout == R.layout.file_details_empty;
352 }
353
354
355 /**
356 * Can be used to get the file that is currently being displayed.
357 * @return The file on the screen.
358 */
359 public OCFile getDisplayedFile(){
360 return mFile;
361 }
362
363 /**
364 * Use this method to signal this Activity that it shall update its view.
365 *
366 * @param file : An {@link OCFile}
367 */
368 public void updateFileDetails(OCFile file, Account ocAccount) {
369 mFile = file;
370 mAccount = ocAccount;
371 updateFileDetails();
372 }
373
374
375 /**
376 * Updates the view with all relevant details about that file.
377 */
378 public void updateFileDetails() {
379
380 if (mFile != null && mAccount != null && mLayout == R.layout.file_details_fragment) {
381
382 // set file details
383 setFilename(mFile.getFileName());
384 setFiletype(DisplayUtils.convertMIMEtoPrettyPrint(mFile
385 .getMimetype()));
386 setFilesize(mFile.getFileLength());
387 if(ocVersionSupportsTimeCreated()){
388 setTimeCreated(mFile.getCreationTimestamp());
389 }
390
391 setTimeModified(mFile.getModificationTimestamp());
392
393 CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
394 cb.setChecked(mFile.keepInSync());
395
396 // configure UI for depending upon local state of the file
397 if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
398 setButtonsForTransferring();
399
400 } else if (mFile.isDown()) {
401 // Update preview
402 if (mFile.getMimetype().startsWith("image/")) {
403 BitmapLoader bl = new BitmapLoader();
404 bl.execute(new String[]{mFile.getStoragePath()});
405 }
406
407 setButtonsForDown();
408
409 } else {
410 setButtonsForRemote();
411 }
412 }
413 }
414
415
416 /**
417 * Updates the filename in view
418 * @param filename to set
419 */
420 private void setFilename(String filename) {
421 TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
422 if (tv != null)
423 tv.setText(filename);
424 }
425
426 /**
427 * Updates the MIME type in view
428 * @param mimetype to set
429 */
430 private void setFiletype(String mimetype) {
431 TextView tv = (TextView) getView().findViewById(R.id.fdType);
432 if (tv != null)
433 tv.setText(mimetype);
434 }
435
436 /**
437 * Updates the file size in view
438 * @param filesize in bytes to set
439 */
440 private void setFilesize(long filesize) {
441 TextView tv = (TextView) getView().findViewById(R.id.fdSize);
442 if (tv != null)
443 tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
444 }
445
446 /**
447 * Updates the time that the file was created in view
448 * @param milliseconds Unix time to set
449 */
450 private void setTimeCreated(long milliseconds){
451 TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
452 TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
453 if(tv != null){
454 tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
455 tv.setVisibility(View.VISIBLE);
456 tvLabel.setVisibility(View.VISIBLE);
457 }
458 }
459
460 /**
461 * Updates the time that the file was last modified
462 * @param milliseconds Unix time to set
463 */
464 private void setTimeModified(long milliseconds){
465 TextView tv = (TextView) getView().findViewById(R.id.fdModified);
466 if(tv != null){
467 tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
468 }
469 }
470
471 /**
472 * Enables or disables buttons for a file being downloaded
473 */
474 private void setButtonsForTransferring() {
475 if (!isEmpty()) {
476 Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
477 //downloadButton.setText(R.string.filedetails_download_in_progress); // ugly
478 downloadButton.setEnabled(false); // TODO replace it with a 'cancel download' button
479
480 // let's protect the user from himself ;)
481 ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false);
482 ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(false);
483 ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(false);
484 }
485 }
486
487 /**
488 * Enables or disables buttons for a file locally available
489 */
490 private void setButtonsForDown() {
491 if (!isEmpty()) {
492 Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
493 //downloadButton.setText(R.string.filedetails_redownload); // ugly
494 downloadButton.setEnabled(true);
495
496 ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(true);
497 ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);
498 ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true);
499 }
500 }
501
502 /**
503 * Enables or disables buttons for a file not locally available
504 */
505 private void setButtonsForRemote() {
506 if (!isEmpty()) {
507 Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
508 //downloadButton.setText(R.string.filedetails_download); // unnecessary
509 downloadButton.setEnabled(true);
510
511 ((Button) getView().findViewById(R.id.fdOpenBtn)).setEnabled(false);
512 ((Button) getView().findViewById(R.id.fdRenameBtn)).setEnabled(true);
513 ((Button) getView().findViewById(R.id.fdRemoveBtn)).setEnabled(true);
514 }
515 }
516
517
518 /**
519 * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
520 * the time that the file was created. There is a chance that this will
521 * be fixed in future versions. Use this method to check if this version of
522 * ownCloud has this fix.
523 * @return True, if ownCloud the ownCloud version is supporting creation time
524 */
525 private boolean ocVersionSupportsTimeCreated(){
526 /*if(mAccount != null){
527 AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
528 OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
529 .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
530 if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
531 return true;
532 }
533 }*/
534 return false;
535 }
536
537
538 /**
539 * Interface to implement by any Activity that includes some instance of FileDetailFragment
540 *
541 * @author David A. Velasco
542 */
543 public interface ContainerActivity {
544
545 /**
546 * Callback method invoked when the detail fragment wants to notice its container
547 * activity about a relevant state the file shown by the fragment.
548 *
549 * Added to notify to FileDisplayActivity about the need of refresh the files list.
550 *
551 * Currently called when:
552 * - a download is started;
553 * - a rename is completed;
554 * - a deletion is completed;
555 * - the 'inSync' flag is changed;
556 */
557 public void onFileStateChanged();
558
559 }
560
561
562 /**
563 * Once the file download has finished -> update view
564 * @author Bartek Przybylski
565 */
566 private class DownloadFinishReceiver extends BroadcastReceiver {
567 @Override
568 public void onReceive(Context context, Intent intent) {
569 String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
570
571 if (!isEmpty() && accountName.equals(mAccount.name)) {
572 boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
573 String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
574 if (mFile.getRemotePath().equals(downloadedRemotePath)) {
575 if (downloadWasFine) {
576 mFile.setStoragePath(intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH)); // updates the local object without accessing the database again
577 }
578 updateFileDetails(); // it updates the buttons; must be called although !downloadWasFine
579 }
580 }
581 }
582 }
583
584
585 /**
586 * Once the file upload has finished -> update view
587 *
588 * Being notified about the finish of an upload is necessary for the next sequence:
589 * 1. Upload a big file.
590 * 2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list
591 * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload.
592 * 3. Click the file in the list to see its details.
593 * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.
594 */
595 private class UploadFinishReceiver extends BroadcastReceiver {
596 @Override
597 public void onReceive(Context context, Intent intent) {
598 String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
599
600 if (!isEmpty() && accountName.equals(mAccount.name)) {
601 boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);
602 String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
603 if (mFile.getRemotePath().equals(uploadRemotePath)) {
604 if (uploadWasFine) {
605 FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
606 mFile = fdsm.getFileByPath(mFile.getRemotePath());
607 }
608 updateFileDetails(); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server
609 }
610 }
611 }
612 }
613
614
615 // this is a temporary class for sharing purposes, it need to be replaced in transfer service
616 private class ShareRunnable implements Runnable {
617 private String mPath;
618
619 public ShareRunnable(String path) {
620 mPath = path;
621 }
622
623 public void run() {
624 AccountManager am = AccountManager.get(getActivity());
625 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
626 OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(account, AccountAuthenticator.KEY_OC_VERSION));
627 String url = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + AccountUtils.getWebdavPath(ocv);
628
629 Log.d("share", "sharing for version " + ocv.toString());
630
631 if (ocv.compareTo(new OwnCloudVersion(0x040000)) >= 0) {
632 String APPS_PATH = "/apps/files_sharing/";
633 String SHARE_PATH = "ajax/share.php";
634
635 String SHARED_PATH = "/apps/files_sharing/get.php?token=";
636
637 final String WEBDAV_SCRIPT = "webdav.php";
638 final String WEBDAV_FILES_LOCATION = "/files/";
639
640 WebdavClient wc = new WebdavClient();
641 HttpConnectionManagerParams params = new HttpConnectionManagerParams();
642 params.setMaxConnectionsPerHost(wc.getHostConfiguration(), 5);
643
644 //wc.getParams().setParameter("http.protocol.single-cookie-header", true);
645 //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
646
647 PostMethod post = new PostMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + APPS_PATH + SHARE_PATH);
648
649 post.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" );
650 post.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
651 List<NameValuePair> formparams = new ArrayList<NameValuePair>();
652 Log.d("share", mPath+"");
653 formparams.add(new BasicNameValuePair("sources",mPath));
654 formparams.add(new BasicNameValuePair("uid_shared_with", "public"));
655 formparams.add(new BasicNameValuePair("permissions", "0"));
656 post.setRequestEntity(new StringRequestEntity(URLEncodedUtils.format(formparams, HTTP.UTF_8)));
657
658 int status;
659 try {
660 PropFindMethod find = new PropFindMethod(url+"/");
661 find.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
662 Log.d("sharer", ""+ url+"/");
663 wc.setCredentials(account.name.substring(0, account.name.lastIndexOf('@')), am.getPassword(account));
664
665 for (org.apache.commons.httpclient.Header a : find.getRequestHeaders()) {
666 Log.d("sharer-h", a.getName() + ":"+a.getValue());
667 }
668
669 int status2 = wc.executeMethod(find);
670
671 Log.d("sharer", "propstatus "+status2);
672
673 GetMethod get = new GetMethod(am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + "/");
674 get.addRequestHeader("Referer", am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL));
675
676 status2 = wc.executeMethod(get);
677
678 Log.d("sharer", "getstatus "+status2);
679 Log.d("sharer", "" + get.getResponseBodyAsString());
680
681 for (org.apache.commons.httpclient.Header a : get.getResponseHeaders()) {
682 Log.d("sharer", a.getName() + ":"+a.getValue());
683 }
684
685 status = wc.executeMethod(post);
686 for (org.apache.commons.httpclient.Header a : post.getRequestHeaders()) {
687 Log.d("sharer-h", a.getName() + ":"+a.getValue());
688 }
689 for (org.apache.commons.httpclient.Header a : post.getResponseHeaders()) {
690 Log.d("sharer", a.getName() + ":"+a.getValue());
691 }
692 String resp = post.getResponseBodyAsString();
693 Log.d("share", ""+post.getURI().toString());
694 Log.d("share", "returned status " + status);
695 Log.d("share", " " +resp);
696
697 if(status != HttpStatus.SC_OK ||resp == null || resp.equals("") || resp.startsWith("false")) {
698 return;
699 }
700
701 JSONObject jsonObject = new JSONObject (resp);
702 String jsonStatus = jsonObject.getString("status");
703 if(!jsonStatus.equals("success")) throw new Exception("Error while sharing file status != success");
704
705 String token = jsonObject.getString("data");
706 String uri = am.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL) + SHARED_PATH + token;
707 Log.d("Actions:shareFile ok", "url: " + uri);
708
709 } catch (HttpException e) {
710 // TODO Auto-generated catch block
711 e.printStackTrace();
712 } catch (IOException e) {
713 // TODO Auto-generated catch block
714 e.printStackTrace();
715 } catch (JSONException e) {
716 // TODO Auto-generated catch block
717 e.printStackTrace();
718 } catch (Exception e) {
719 // TODO Auto-generated catch block
720 e.printStackTrace();
721 }
722
723 } else if (ocv.compareTo(new OwnCloudVersion(0x030000)) >= 0) {
724
725 }
726 }
727 }
728
729 public void onDismiss(EditNameFragment dialog) {
730 if (dialog instanceof EditNameFragment) {
731 if (((EditNameFragment)dialog).getResult()) {
732 String newFilename = ((EditNameFragment)dialog).getNewFilename();
733 Log.d(TAG, "name edit dialog dismissed with new name " + newFilename);
734 if (!newFilename.equals(mFile.getFileName())) {
735 FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getContentResolver());
736 if (fdsm.getFileById(mFile.getFileId()) != null) {
737 OCFile newFile = new OCFile(fdsm.getFileById(mFile.getParentId()).getRemotePath() + newFilename);
738 newFile.setCreationTimestamp(mFile.getCreationTimestamp());
739 newFile.setFileId(mFile.getFileId());
740 newFile.setFileLength(mFile.getFileLength());
741 newFile.setKeepInSync(mFile.keepInSync());
742 newFile.setLastSyncDate(mFile.getLastSyncDate());
743 newFile.setMimetype(mFile.getMimetype());
744 newFile.setModificationTimestamp(mFile.getModificationTimestamp());
745 newFile.setParentId(mFile.getParentId());
746 if (mFile.isDown()) {
747 File f = new File(mFile.getStoragePath());
748 Log.e(TAG, f.getAbsolutePath());
749 f.renameTo(new File(f.getParent() + File.separator + newFilename)); // TODO check if fails
750 Log.e(TAG, f.getParent() + File.separator + newFilename);
751 newFile.setStoragePath(f.getParent() + File.separator + newFilename);
752 }
753
754 new Thread(new RenameRunnable(mFile, newFile, mAccount, new Handler())).start();
755
756 }
757 }
758 }
759 } else {
760 Log.e(TAG, "Unknown dialog instance passed to onDismissDalog: " + dialog.getClass().getCanonicalName());
761 }
762
763 }
764
765 private class RenameRunnable implements Runnable {
766
767 Account mAccount;
768 OCFile mOld, mNew;
769 Handler mHandler;
770
771 public RenameRunnable(OCFile oldFile, OCFile newFile, Account account, Handler handler) {
772 mOld = oldFile;
773 mNew = newFile;
774 mAccount = account;
775 mHandler = handler;
776 }
777
778 public void run() {
779 WebdavClient wc = new WebdavClient(mAccount, getSherlockActivity().getApplicationContext());
780 wc.allowSelfsignedCertificates();
781 AccountManager am = AccountManager.get(getSherlockActivity());
782 String baseUrl = am.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
783 OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
784 String webdav_path = AccountUtils.getWebdavPath(ocv);
785 Log.d("ASD", ""+baseUrl + webdav_path + WebdavUtils.encodePath(mOld.getRemotePath()));
786
787 Log.e("ASD", Uri.parse(baseUrl).getPath() == null ? "" : Uri.parse(baseUrl).getPath() + webdav_path + WebdavUtils.encodePath(mNew.getRemotePath()));
788 LocalMoveMethod move = new LocalMoveMethod(baseUrl + webdav_path + WebdavUtils.encodePath(mOld.getRemotePath()),
789 Uri.parse(baseUrl).getPath() == null ? "" : Uri.parse(baseUrl).getPath() + webdav_path + WebdavUtils.encodePath(mNew.getRemotePath()));
790
791 try {
792 int status = wc.executeMethod(move);
793 if (move.succeeded()) {
794 FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getContentResolver());
795 fdsm.removeFile(mOld);
796 fdsm.saveFile(mNew);
797 mFile = mNew;
798 mHandler.post(new Runnable() {
799 @Override
800 public void run() {
801 updateFileDetails(mFile, mAccount);
802 mContainerActivity.onFileStateChanged();
803 }
804 });
805 }
806 Log.e("ASD", ""+move.getQueryString());
807 Log.d("move", "returned status " + status);
808 } catch (HttpException e) {
809 // TODO Auto-generated catch block
810 e.printStackTrace();
811 } catch (IOException e) {
812 // TODO Auto-generated catch block
813 e.printStackTrace();
814 }
815 }
816 private class LocalMoveMethod extends DavMethodBase {
817
818 public LocalMoveMethod(String uri, String dest) {
819 super(uri);
820 addRequestHeader(new org.apache.commons.httpclient.Header("Destination", dest));
821 }
822
823 @Override
824 public String getName() {
825 return "MOVE";
826 }
827
828 @Override
829 protected boolean isSuccess(int status) {
830 return status == 201 || status == 204;
831 }
832
833 }
834 }
835
836 private static class EditNameFragment extends SherlockDialogFragment implements OnClickListener {
837
838 private String mNewFilename;
839 private boolean mResult;
840 private FileDetailFragment mListener;
841
842 static public EditNameFragment newInstance(String filename) {
843 EditNameFragment f = new EditNameFragment();
844 Bundle args = new Bundle();
845 args.putString("filename", filename);
846 f.setArguments(args);
847 return f;
848 }
849
850 @Override
851 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
852 View v = inflater.inflate(R.layout.edit_box_dialog, container, false);
853
854 String currentName = getArguments().getString("filename");
855 if (currentName == null)
856 currentName = "";
857
858 ((Button)v.findViewById(R.id.cancel)).setOnClickListener(this);
859 ((Button)v.findViewById(R.id.ok)).setOnClickListener(this);
860 ((TextView)v.findViewById(R.id.user_input)).setText(currentName);
861 ((TextView)v.findViewById(R.id.user_input)).requestFocus();
862 getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
863
864 mResult = false;
865 return v;
866 }
867
868 @Override
869 public void onClick(View view) {
870 switch (view.getId()) {
871 case R.id.ok: {
872 mNewFilename = ((TextView)getView().findViewById(R.id.user_input)).getText().toString();
873 mResult = true;
874 }
875 case R.id.cancel: { // fallthought
876 dismiss();
877 mListener.onDismiss(this);
878 }
879 }
880 }
881
882 void setOnDismissListener(FileDetailFragment listener) {
883 mListener = listener;
884 }
885
886 public String getNewFilename() {
887 return mNewFilename;
888 }
889
890 // true if user click ok
891 public boolean getResult() {
892 return mResult;
893 }
894
895 }
896
897 private class RemoveRunnable implements Runnable {
898
899 /** Arbitrary timeout for deletion */
900 public final static int DELETION_TIMEOUT = 5000;
901
902 Account mAccount;
903 OCFile mFileToRemove;
904 Handler mHandler;
905
906 public RemoveRunnable(OCFile fileToRemove, Account account, Handler handler) {
907 mFileToRemove = fileToRemove;
908 mAccount = account;
909 mHandler = handler;
910 }
911
912 public void run() {
913 WebdavClient wc = new WebdavClient(mAccount, getSherlockActivity().getApplicationContext());
914 wc.allowSelfsignedCertificates();
915 AccountManager am = AccountManager.get(getSherlockActivity());
916 String baseUrl = am.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
917 OwnCloudVersion ocv = new OwnCloudVersion(am.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
918 String webdav_path = AccountUtils.getWebdavPath(ocv);
919 Log.d("ASD", ""+baseUrl + webdav_path + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
920
921 DeleteMethod delete = new DeleteMethod(baseUrl + webdav_path + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
922
923 boolean success = false;
924 try {
925 int status = wc.executeMethod(delete, DELETION_TIMEOUT);
926 if (delete.succeeded()) {
927 FileDataStorageManager fdsm = new FileDataStorageManager(mAccount, getActivity().getContentResolver());
928 fdsm.removeFile(mFileToRemove);
929 mHandler.post(new Runnable() {
930 @Override
931 public void run() {
932 try {
933 Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
934 msg.show();
935 if (getActivity() instanceof FileDisplayActivity) {
936 // double pane
937 FragmentTransaction transaction = getActivity().getSupportFragmentManager().beginTransaction();
938 transaction.replace(R.id.file_details_container, new FileDetailFragment(null, null)); // empty FileDetailFragment
939 transaction.commit();
940 mContainerActivity.onFileStateChanged();
941
942 } else {
943 getActivity().finish();
944 }
945
946 } catch (NotFoundException e) {
947 e.printStackTrace();
948 }
949 }
950 });
951 success = true;
952 }
953 Log.e("ASD", ""+ delete.getQueryString());
954 Log.d("delete", "returned status " + status);
955
956 } catch (HttpException e) {
957 e.printStackTrace();
958
959 } catch (IOException e) {
960 e.printStackTrace();
961
962 } finally {
963 if (!success) {
964 mHandler.post(new Runnable() {
965 @Override
966 public void run() {
967 try {
968 Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
969 msg.show();
970
971 } catch (NotFoundException e) {
972 e.printStackTrace();
973 }
974 }
975 });
976 }
977 }
978 }
979
980 }
981
982 class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
983 @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20
984 @Override
985 protected Bitmap doInBackground(String... params) {
986 Bitmap result = null;
987 if (params.length != 1) return result;
988 String storagePath = params[0];
989 try {
990
991 BitmapFactory.Options options = new Options();
992 options.inScaled = true;
993 options.inPurgeable = true;
994 options.inJustDecodeBounds = true;
995 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
996 options.inPreferQualityOverSpeed = false;
997 }
998 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
999 options.inMutable = false;
1000 }
1001
1002 result = BitmapFactory.decodeFile(storagePath, options);
1003 options.inJustDecodeBounds = false;
1004
1005 int width = options.outWidth;
1006 int height = options.outHeight;
1007 int scale = 1;
1008 if (width >= 2048 || height >= 2048) {
1009 scale = (int) Math.ceil((Math.ceil(Math.max(height, width) / 2048.)));
1010 options.inSampleSize = scale;
1011 }
1012 Display display = getActivity().getWindowManager().getDefaultDisplay();
1013 Point size = new Point();
1014 int screenwidth;
1015 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
1016 display.getSize(size);
1017 screenwidth = size.x;
1018 } else {
1019 screenwidth = display.getWidth();
1020 }
1021
1022 Log.e("ASD", "W " + width + " SW " + screenwidth);
1023
1024 if (width > screenwidth) {
1025 scale = (int) Math.ceil((float)width / screenwidth);
1026 options.inSampleSize = scale;
1027 }
1028
1029 result = BitmapFactory.decodeFile(storagePath, options);
1030
1031 Log.e("ASD", "W " + options.outWidth + " SW " + options.outHeight);
1032
1033 } catch (OutOfMemoryError e) {
1034 result = null;
1035 Log.e(TAG, "Out of memory occured for file with size " + storagePath);
1036
1037 } catch (NoSuchFieldError e) {
1038 result = null;
1039 Log.e(TAG, "Error from access to unexisting field despite protection " + storagePath);
1040
1041 } catch (Throwable t) {
1042 result = null;
1043 Log.e(TAG, "Unexpected error while creating image preview " + storagePath, t);
1044 }
1045 return result;
1046 }
1047 @Override
1048 protected void onPostExecute(Bitmap result) {
1049 if (result != null && mPreview != null) {
1050 mPreview.setImageBitmap(result);
1051 }
1052 }
1053
1054 }
1055
1056
1057 }