9bcf9deeffb4c4a5017cc64fc1339de931d0c99c
[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.util.List;
21
22 import android.accounts.Account;
23 import android.accounts.AccountManager;
24 import android.app.ActionBar.LayoutParams;
25 import android.content.ActivityNotFoundException;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.pm.PackageManager;
31 import android.graphics.Bitmap;
32 import android.graphics.BitmapFactory;
33 import android.graphics.BitmapFactory.Options;
34 import android.graphics.Path.FillType;
35 import android.net.Uri;
36 import android.os.Bundle;
37 import android.util.Log;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.View.OnClickListener;
41 import android.view.ViewGroup;
42 import android.webkit.MimeTypeMap;
43 import android.widget.Button;
44 import android.widget.ImageView;
45 import android.widget.TextView;
46 import android.widget.Toast;
47
48 import com.actionbarsherlock.app.SherlockFragment;
49
50 import eu.alefzero.owncloud.DisplayUtils;
51 import eu.alefzero.owncloud.R;
52 import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
53 import eu.alefzero.owncloud.datamodel.OCFile;
54 import eu.alefzero.owncloud.files.services.FileDownloader;
55 import eu.alefzero.owncloud.utils.OwnCloudVersion;
56
57 /**
58 * This Fragment is used to display the details about a file.
59 *
60 * @author Bartek Przybylski
61 *
62 */
63 public class FileDetailFragment extends SherlockFragment implements
64 OnClickListener {
65
66 public static final String EXTRA_FILE = "FILE";
67
68 private DownloadFinishReceiver mDownloadFinishReceiver;
69 private Intent mIntent;
70 private int mLayout;
71 private View mView;
72 private OCFile mFile;
73 private static final String TAG = "FileDetailFragment";
74
75 /**
76 * Default constructor - contains real layout
77 */
78 public FileDetailFragment(){
79 mLayout = R.layout.file_details_fragment;
80 }
81
82 /**
83 * Creates a dummy layout. For use if the user never has
84 * tapped on a file before
85 *
86 * @param useEmptyView If true, use empty layout
87 */
88 public FileDetailFragment(boolean useEmptyView){
89 if(useEmptyView){
90 mLayout = R.layout.file_details_empty;
91 } else {
92 mLayout = R.layout.file_details_fragment;
93 }
94 }
95
96 /**
97 * Use this when creating the fragment and display
98 * a file at the same time
99 *
100 * @param showDetailsIntent The Intent with the required parameters
101 * @see FileDetailFragment#updateFileDetails(Intent)
102 */
103 public FileDetailFragment(Intent showDetailsIntent) {
104 mIntent = showDetailsIntent;
105 mLayout = R.layout.file_details_fragment;
106 }
107
108 @Override
109 public void onResume() {
110 super.onResume();
111 mDownloadFinishReceiver = new DownloadFinishReceiver();
112 IntentFilter filter = new IntentFilter(
113 FileDownloader.DOWNLOAD_FINISH_MESSAGE);
114 getActivity().registerReceiver(mDownloadFinishReceiver, filter);
115 }
116
117 @Override
118 public void onPause() {
119 super.onPause();
120 getActivity().unregisterReceiver(mDownloadFinishReceiver);
121 mDownloadFinishReceiver = null;
122 }
123
124 @Override
125 public View onCreateView(LayoutInflater inflater, ViewGroup container,
126 Bundle savedInstanceState) {
127 View view = null;
128 view = inflater.inflate(mLayout, container, false);
129 mView = view;
130 if(mLayout == R.layout.file_details_fragment){
131 // Phones will launch an activity with this intent
132 if(mIntent == null){
133 mIntent = getActivity().getIntent();
134 }
135 updateFileDetails();
136 }
137
138 return view;
139 }
140
141 @Override
142 public View getView() {
143 return super.getView() == null ? mView : super.getView();
144 }
145
146 @Override
147 public void onClick(View v) {
148 Toast.makeText(getActivity(), "Downloading", Toast.LENGTH_LONG).show();
149 Intent i = new Intent(getActivity(), FileDownloader.class);
150 i.putExtra(FileDownloader.EXTRA_ACCOUNT,
151 mIntent.getParcelableExtra(FileDownloader.EXTRA_ACCOUNT));
152 i.putExtra(FileDownloader.EXTRA_REMOTE_PATH, mFile.getRemotePath());
153 i.putExtra(FileDownloader.EXTRA_FILE_PATH, mFile.getURLDecodedRemotePath());
154 i.putExtra(FileDownloader.EXTRA_FILE_SIZE, mFile.getFileLength());
155 v.setEnabled(false);
156 getActivity().startService(i);
157 }
158
159 /**
160 * Can be used to get the file that is currently being displayed.
161 * @return The file on the screen.
162 */
163 public OCFile getDisplayedFile(){
164 return mFile;
165 }
166
167 /**
168 * Use this method to signal this Activity that it shall update its view.
169 *
170 * @param intent The {@link Intent} that contains extra information about
171 * this file The intent needs to have these extras:
172 * <p>
173 *
174 * {@link FileDetailFragment#EXTRA_FILE}: An {@link OCFile}
175 * {@link FileDownloader#EXTRA_ACCOUNT}: The Account that file
176 * belongs to (required for downloading)
177 */
178 public void updateFileDetails(Intent intent) {
179 mIntent = intent;
180 updateFileDetails();
181 }
182
183 /**
184 * Updates the view with all relevant details about that file.
185 */
186 private void updateFileDetails() {
187 mFile = mIntent.getParcelableExtra(EXTRA_FILE);
188 Button downloadButton = (Button) getView().findViewById(R.id.fdDownloadBtn);
189
190 if (mFile != null) {
191 // set file details
192 setFilename(mFile.getFileName());
193 setFiletype(DisplayUtils.convertMIMEtoPrettyPrint(mFile
194 .getMimetype()));
195 setFilesize(mFile.getFileLength());
196 if(ocVersionSupportsTimeCreated()){
197 setTimeCreated(mFile.getCreationTimestamp());
198 }
199
200 setTimeModified(mFile.getModificationTimestamp());
201
202 // Update preview
203 if (mFile.getStoragePath() != null) {
204 ImageView preview = (ImageView) getView().findViewById(R.id.fdPreview);
205 try {
206 if (mFile.getMimetype().startsWith("image/")) {
207 BitmapFactory.Options options = new Options();
208 options.inScaled = true;
209 options.inPurgeable = true;
210 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
211 options.inPreferQualityOverSpeed = false;
212 }
213 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
214 options.inMutable = false;
215 }
216
217 Bitmap bmp = BitmapFactory.decodeFile(mFile.getStoragePath(), options);
218
219 int width = options.outWidth;
220 int height = options.outHeight;
221 int scale = 1;
222 if (width >= 2048 || height >= 2048) {
223 scale = (int) (Math.ceil(Math.max(height, width)/2048.));
224 options.inSampleSize = scale;
225 bmp.recycle();
226
227 bmp = BitmapFactory.decodeFile(mFile.getStoragePath(), options);
228 }
229 preview.setImageBitmap(bmp);
230 }
231 } catch (OutOfMemoryError e) {
232 preview.setVisibility(View.INVISIBLE);
233 Log.e(TAG, "Out of memory occured for file with size " + mFile.getFileLength());
234
235 } catch (NoSuchFieldError e) {
236 preview.setVisibility(View.INVISIBLE);
237 Log.e(TAG, "Error from access to unexisting field despite protection " + mFile.getFileLength());
238
239 } catch (Throwable t) {
240 preview.setVisibility(View.INVISIBLE);
241 Log.e(TAG, "Unexpected error while creating image preview " + mFile.getFileLength(), t);
242 }
243 downloadButton.setText(R.string.filedetails_open);
244 downloadButton.setOnClickListener(new OnClickListener() {
245 @Override
246 public void onClick(View v) {
247 String storagePath = mFile.getStoragePath();
248 try {
249 Intent i = new Intent(Intent.ACTION_VIEW);
250 i.setDataAndType(Uri.parse("file://"+ storagePath), mFile.getMimetype());
251 i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
252 startActivity(i);
253
254 } catch (Throwable t) {
255 Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
256 boolean toastIt = true;
257 String mimeType = "";
258 try {
259 Intent i = new Intent(Intent.ACTION_VIEW);
260 mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
261 if (mimeType != mFile.getMimetype()) {
262 i.setDataAndType(Uri.parse("file://"+mFile.getStoragePath()), mimeType);
263 i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
264 startActivity(i);
265 toastIt = false;
266 }
267
268 } catch (IndexOutOfBoundsException e) {
269 Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
270
271 } catch (ActivityNotFoundException e) {
272 Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
273
274 } catch (Throwable th) {
275 Log.e(TAG, "Unexpected problem when opening: " + storagePath, th);
276
277 } finally {
278 if (toastIt) {
279 Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
280 }
281 }
282
283 }
284 }
285 });
286 } else {
287 // Make download button effective
288 downloadButton.setOnClickListener(this);
289 }
290 }
291 }
292
293 /**
294 * Updates the filename in view
295 * @param filename to set
296 */
297 private void setFilename(String filename) {
298 TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
299 if (tv != null)
300 tv.setText(filename);
301 }
302
303 /**
304 * Updates the MIME type in view
305 * @param mimetype to set
306 */
307 private void setFiletype(String mimetype) {
308 TextView tv = (TextView) getView().findViewById(R.id.fdType);
309 if (tv != null)
310 tv.setText(mimetype);
311 }
312
313 /**
314 * Updates the file size in view
315 * @param filesize in bytes to set
316 */
317 private void setFilesize(long filesize) {
318 TextView tv = (TextView) getView().findViewById(R.id.fdSize);
319 if (tv != null)
320 tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
321 }
322
323 /**
324 * Updates the time that the file was created in view
325 * @param milliseconds Unix time to set
326 */
327 private void setTimeCreated(long milliseconds){
328 TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
329 TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
330 if(tv != null){
331 tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
332 tv.setVisibility(View.VISIBLE);
333 tvLabel.setVisibility(View.VISIBLE);
334 }
335 }
336
337 /**
338 * Updates the time that the file was last modified
339 * @param milliseconds Unix time to set
340 */
341 private void setTimeModified(long milliseconds){
342 TextView tv = (TextView) getView().findViewById(R.id.fdModified);
343 if(tv != null){
344 tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
345 }
346 }
347
348 /**
349 * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
350 * the time that the file was created. There is a chance that this will
351 * be fixed in future versions. Use this method to check if this version of
352 * ownCloud has this fix.
353 * @return True, if ownCloud the ownCloud version is supporting creationg time
354 */
355 private boolean ocVersionSupportsTimeCreated(){
356 /* if(mIntent != null){
357 Account ocAccount = mIntent.getParcelableExtra(FileDownloader.EXTRA_ACCOUNT);
358 if(ocAccount != null){
359 AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
360 OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
361 .getUserData(ocAccount, AccountAuthenticator.KEY_OC_VERSION));
362 if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
363 return true;
364 }
365 }
366 }*/
367 return false;
368 }
369
370 /**
371 * Once the file download has finished -> update view
372 * @author Bartek Przybylski
373 */
374 private class DownloadFinishReceiver extends BroadcastReceiver {
375 @Override
376 public void onReceive(Context context, Intent intent) {
377 getView().findViewById(R.id.fdDownloadBtn).setEnabled(true);
378 if (intent.getAction().equals(FileDownloader.BAD_DOWNLOAD_MESSAGE)) {
379 Toast.makeText(context, R.string.downloader_download_failed , Toast.LENGTH_SHORT).show();
380
381 } else if (intent.getAction().equals(FileDownloader.DOWNLOAD_FINISH_MESSAGE)) {
382 ((OCFile)mIntent.getParcelableExtra(EXTRA_FILE)).setStoragePath(intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH));
383 updateFileDetails(mIntent);
384 }
385 }
386
387 }
388
389 }