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