Merge branch '559_longpress_filename' of https://github.com/owncloud/android into...
[pub/Android/ownCloud.git] / src / com / owncloud / android / utils / DisplayUtils.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author Bartek Przybylski
5 * @author David A. Velasco
6 * Copyright (C) 2011 Bartek Przybylski
7 * Copyright (C) 2015 ownCloud Inc.
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2,
11 * as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 package com.owncloud.android.utils;
24
25 import java.math.BigDecimal;
26 import java.net.IDN;
27 import java.text.DateFormat;
28 import java.util.Arrays;
29 import java.util.Calendar;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Set;
34 import java.util.Vector;
35
36 import android.annotation.TargetApi;
37 import android.app.Activity;
38 import android.content.Context;
39 import android.graphics.Color;
40 import android.graphics.Point;
41 import android.graphics.PorterDuff;
42 import android.os.Build;
43 import android.text.format.DateUtils;
44 import android.view.Display;
45 import android.webkit.MimeTypeMap;
46 import android.widget.ProgressBar;
47 import android.widget.SeekBar;
48
49 import com.owncloud.android.MainApp;
50 import com.owncloud.android.R;
51 import com.owncloud.android.datamodel.OCFile;
52
53 /**
54 * A helper class for some string operations.
55 */
56 public class DisplayUtils {
57
58 private static final String OWNCLOUD_APP_NAME = "ownCloud";
59
60 //private static String TAG = DisplayUtils.class.getSimpleName();
61
62 private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
63 private static final int[] sizeScales = { 0, 0, 0, 1, 1, 2, 2, 2, 2 };
64
65 private static HashMap<String, String> mimeType2HUmanReadable;
66 static {
67 mimeType2HUmanReadable = new HashMap<String, String>();
68 // images
69 mimeType2HUmanReadable.put("image/jpeg", "JPEG image");
70 mimeType2HUmanReadable.put("image/jpg", "JPEG image");
71 mimeType2HUmanReadable.put("image/png", "PNG image");
72 mimeType2HUmanReadable.put("image/bmp", "Bitmap image");
73 mimeType2HUmanReadable.put("image/gif", "GIF image");
74 mimeType2HUmanReadable.put("image/svg+xml", "JPEG image");
75 mimeType2HUmanReadable.put("image/tiff", "TIFF image");
76 // music
77 mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file");
78 mimeType2HUmanReadable.put("application/ogg", "OGG music file");
79
80 }
81
82 private static final String TYPE_APPLICATION = "application";
83 private static final String TYPE_AUDIO = "audio";
84 private static final String TYPE_IMAGE = "image";
85 private static final String TYPE_TXT = "text";
86 private static final String TYPE_VIDEO = "video";
87
88 private static final String SUBTYPE_PDF = "pdf";
89 private static final String SUBTYPE_XML = "xml";
90 private static final String[] SUBTYPES_DOCUMENT = {
91 "msword",
92 "vnd.openxmlformats-officedocument.wordprocessingml.document",
93 "vnd.oasis.opendocument.text",
94 "rtf",
95 "javascript"
96 };
97 private static Set<String> SUBTYPES_DOCUMENT_SET = new HashSet<String>(Arrays.asList(SUBTYPES_DOCUMENT));
98 private static final String[] SUBTYPES_SPREADSHEET = {
99 "msexcel",
100 "vnd.ms-excel",
101 "vnd.openxmlformats-officedocument.spreadsheetml.sheet",
102 "vnd.oasis.opendocument.spreadsheet"
103 };
104 private static Set<String> SUBTYPES_SPREADSHEET_SET = new HashSet<String>(Arrays.asList(SUBTYPES_SPREADSHEET));
105 private static final String[] SUBTYPES_PRESENTATION = {
106 "mspowerpoint",
107 "vnd.ms-powerpoint",
108 "vnd.openxmlformats-officedocument.presentationml.presentation",
109 "vnd.oasis.opendocument.presentation"
110 };
111 private static Set<String> SUBTYPES_PRESENTATION_SET = new HashSet<String>(Arrays.asList(SUBTYPES_PRESENTATION));
112 private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"};
113 private static final Set<String> SUBTYPES_COMPRESSED_SET = new HashSet<String>(Arrays.asList(SUBTYPES_COMPRESSED));
114 private static final String SUBTYPE_OCTET_STREAM = "octet-stream";
115 private static final String EXTENSION_RAR = "rar";
116 private static final String EXTENSION_RTF = "rtf";
117 private static final String EXTENSION_3GP = "3gp";
118 private static final String EXTENSION_PY = "py";
119 private static final String EXTENSION_JS = "js";
120
121 /**
122 * Converts the file size in bytes to human readable output.
123 * <ul>
124 * <li>appends a size suffix, e.g. B, KB, MB etc.</li>
125 * <li>rounds the size based on the suffix to 0,1 or 2 decimals</li>
126 * </ul>
127 *
128 * @param bytes Input file size
129 * @return Like something readable like "12 MB"
130 */
131 public static String bytesToHumanReadable(long bytes) {
132 double result = bytes;
133 int attachedsuff = 0;
134 while (result > 1024 && attachedsuff < sizeSuffixes.length) {
135 result /= 1024.;
136 attachedsuff++;
137 }
138
139 return new BigDecimal(result).setScale(
140 sizeScales[attachedsuff], BigDecimal.ROUND_HALF_UP) + " " + sizeSuffixes[attachedsuff];
141 }
142
143 /**
144 * Converts MIME types like "image/jpg" to more end user friendly output
145 * like "JPG image".
146 *
147 * @param mimetype MIME type to convert
148 * @return A human friendly version of the MIME type
149 */
150 public static String convertMIMEtoPrettyPrint(String mimetype) {
151 if (mimeType2HUmanReadable.containsKey(mimetype)) {
152 return mimeType2HUmanReadable.get(mimetype);
153 }
154 if (mimetype.split("/").length >= 2)
155 return mimetype.split("/")[1].toUpperCase() + " file";
156 return "Unknown type";
157 }
158
159
160 /**
161 * Returns the resource identifier of an image to use as icon associated to a known MIME type.
162 *
163 * @param mimetype MIME type string; if NULL, the method tries to guess it from the extension in filename
164 * @param filename Name, with extension.
165 * @return Identifier of an image resource.
166 */
167 public static int getFileTypeIconId(String mimetype, String filename) {
168
169 if (mimetype == null) {
170 String fileExtension = getExtension(filename);
171 mimetype = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
172 if (mimetype == null) {
173 mimetype = TYPE_APPLICATION + "/" + SUBTYPE_OCTET_STREAM;
174 }
175 }
176
177 if ("DIR".equals(mimetype)) {
178 return R.drawable.ic_menu_archive;
179
180 } else {
181 String [] parts = mimetype.split("/");
182 String type = parts[0];
183 String subtype = (parts.length > 1) ? parts[1] : "";
184
185 if(TYPE_TXT.equals(type)) {
186 return R.drawable.file_doc;
187
188 } else if(TYPE_IMAGE.equals(type)) {
189 return R.drawable.file_image;
190
191 } else if(TYPE_VIDEO.equals(type)) {
192 return R.drawable.file_movie;
193
194 } else if(TYPE_AUDIO.equals(type)) {
195 return R.drawable.file_sound;
196
197 } else if(TYPE_APPLICATION.equals(type)) {
198
199 if (SUBTYPE_PDF.equals(subtype)) {
200 return R.drawable.file_pdf;
201
202 } else if (SUBTYPE_XML.equals(subtype)) {
203 return R.drawable.file_doc;
204
205 } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) {
206 return R.drawable.file_doc;
207
208 } else if (SUBTYPES_SPREADSHEET_SET.contains(subtype)) {
209 return R.drawable.file_xls;
210
211 } else if (SUBTYPES_PRESENTATION_SET.contains(subtype)) {
212 return R.drawable.file_ppt;
213
214 } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) {
215 return R.drawable.file_zip;
216
217 } else if (SUBTYPE_OCTET_STREAM.equals(subtype) ) {
218 if (getExtension(filename).equalsIgnoreCase(EXTENSION_RAR)) {
219 return R.drawable.file_zip;
220
221 } else if (getExtension(filename).equalsIgnoreCase(EXTENSION_RTF)) {
222 return R.drawable.file_doc;
223
224 } else if (getExtension(filename).equalsIgnoreCase(EXTENSION_3GP)) {
225 return R.drawable.file_movie;
226
227 } else if ( getExtension(filename).equalsIgnoreCase(EXTENSION_PY) ||
228 getExtension(filename).equalsIgnoreCase(EXTENSION_JS)) {
229 return R.drawable.file_doc;
230 }
231 }
232 }
233 }
234
235 // default icon
236 return R.drawable.file;
237 }
238
239
240 private static String getExtension(String filename) {
241 String extension = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
242 return extension;
243 }
244
245 /**
246 * Converts Unix time to human readable format
247 * @param milliseconds that have passed since 01/01/1970
248 * @return The human readable time for the users locale
249 */
250 public static String unixTimeToHumanReadable(long milliseconds) {
251 Date date = new Date(milliseconds);
252 DateFormat df = DateFormat.getDateTimeInstance();
253 return df.format(date);
254 }
255
256
257 public static int getSeasonalIconId() {
258 if (Calendar.getInstance().get(Calendar.DAY_OF_YEAR) >= 354 &&
259 MainApp.getAppContext().getString(R.string.app_name).equals(OWNCLOUD_APP_NAME)) {
260 return R.drawable.winter_holidays_icon;
261 } else {
262 return R.drawable.icon;
263 }
264 }
265
266 /**
267 * Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode.
268 * @param url the URL where the domain name should be converted
269 * @param toASCII if true converts from Unicode to ASCII, if false converts from ASCII to Unicode
270 * @return the URL containing the converted domain name
271 */
272 @TargetApi(Build.VERSION_CODES.GINGERBREAD)
273 public static String convertIdn(String url, boolean toASCII) {
274
275 String urlNoDots = url;
276 String dots="";
277 while (urlNoDots.startsWith(".")) {
278 urlNoDots = url.substring(1);
279 dots = dots + ".";
280 }
281
282 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
283 // Find host name after '//' or '@'
284 int hostStart = 0;
285 if (urlNoDots.indexOf("//") != -1) {
286 hostStart = url.indexOf("//") + "//".length();
287 } else if (url.indexOf("@") != -1) {
288 hostStart = url.indexOf("@") + "@".length();
289 }
290
291 int hostEnd = url.substring(hostStart).indexOf("/");
292 // Handle URL which doesn't have a path (path is implicitly '/')
293 hostEnd = (hostEnd == -1 ? urlNoDots.length() : hostStart + hostEnd);
294
295 String host = urlNoDots.substring(hostStart, hostEnd);
296 host = (toASCII ? IDN.toASCII(host) : IDN.toUnicode(host));
297
298 return dots + urlNoDots.substring(0, hostStart) + host + urlNoDots.substring(hostEnd);
299 } else {
300 return dots + url;
301 }
302 }
303
304 /**
305 * Get the file extension if it is on path as type "content://.../DocInfo.doc"
306 * @param filepath: Content Uri converted to string format
307 * @return String: fileExtension (type '.pdf'). Empty if no extension
308 */
309 public static String getComposedFileExtension(String filepath) {
310 String fileExtension = "";
311 String fileNameInContentUri = filepath.substring(filepath.lastIndexOf("/"));
312
313 // Check if extension is included in uri
314 int pos = fileNameInContentUri.lastIndexOf('.');
315 if (pos >= 0) {
316 fileExtension = fileNameInContentUri.substring(pos);
317 }
318 return fileExtension;
319 }
320
321 @SuppressWarnings("deprecation")
322 public static CharSequence getRelativeDateTimeString (
323 Context c, long time, long minResolution, long transitionResolution, int flags
324 ){
325
326 CharSequence dateString = "";
327
328 // in Future
329 if (time > System.currentTimeMillis()){
330 return DisplayUtils.unixTimeToHumanReadable(time);
331 }
332 // < 60 seconds -> seconds ago
333 else if ((System.currentTimeMillis() - time) < 60 * 1000) {
334 return c.getString(R.string.file_list_seconds_ago);
335 } else {
336 // Workaround 2.x bug (see https://github.com/owncloud/android/issues/716)
337 if ( Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB &&
338 (System.currentTimeMillis() - time) > 24 * 60 * 60 * 1000 ) {
339 Date date = new Date(time);
340 date.setHours(0);
341 date.setMinutes(0);
342 date.setSeconds(0);
343 dateString = DateUtils.getRelativeDateTimeString(
344 c, date.getTime(), minResolution, transitionResolution, flags
345 );
346 } else {
347 dateString = DateUtils.getRelativeDateTimeString(c, time, minResolution, transitionResolution, flags);
348 }
349 }
350
351 return dateString.toString().split(",")[0];
352 }
353
354 /**
355 * Update the passed path removing the last "/" if it is not the root folder
356 * @param path
357 */
358 public static String getPathWithoutLastSlash(String path) {
359
360 // Remove last slash from path
361 if (path.length() > 1 && path.charAt(path.length()-1) == OCFile.PATH_SEPARATOR.charAt(0)) {
362 path = path.substring(0, path.length()-1);
363 }
364 return path;
365 }
366
367
368 /**
369 * Gets the screen size in pixels in a backwards compatible way
370 *
371 * @param caller Activity calling; needed to get access to the {@link android.view.WindowManager}
372 * @return Size in pixels of the screen, or default {@link Point} if caller is null
373 */
374 public static Point getScreenSize(Activity caller) {
375 Point size = new Point();
376 if (caller != null) {
377 Display display = caller.getWindowManager().getDefaultDisplay();
378 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
379 display.getSize(size);
380 } else {
381 size.set(display.getWidth(), display.getHeight());
382 }
383 }
384 return size;
385 }
386
387 /**
388 * sets the coloring of the given progress bar to color_accent.
389 *
390 * @param progressBar the progress bar to be colored
391 */
392 public static void colorPreLollipopHorizontalProgressBar(ProgressBar progressBar) {
393 if (progressBar != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
394 int color = progressBar.getResources().getColor(R.color.color_accent);
395 progressBar.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
396 progressBar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
397 }
398 }
399
400 /**
401 * sets the coloring of the given seek bar to color_accent.
402 *
403 * @param seekBar the seek bar to be colored
404 */
405 public static void colorPreLollipopHorizontalSeekBar(SeekBar seekBar) {
406 if (seekBar != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
407 colorPreLollipopHorizontalProgressBar(seekBar);
408
409 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
410 int color = seekBar.getResources().getColor(R.color.color_accent);
411 seekBar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN);
412 seekBar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN);
413 }
414 }
415 }
416 }