2  *   ownCloud Android client application 
   4  *   @author Bartek Przybylski 
   5  *   @author David A. Velasco 
   6  *   Copyright (C) 2011  Bartek Przybylski 
   7  *   Copyright (C) 2015 ownCloud Inc. 
   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. 
  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. 
  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/>. 
  23 package com
.owncloud
.android
.utils
; 
  26 import java
.text
.DateFormat
; 
  27 import java
.util
.Arrays
; 
  28 import java
.util
.Calendar
; 
  29 import java
.util
.Date
; 
  30 import java
.util
.HashMap
; 
  31 import java
.util
.HashSet
; 
  33 import java
.util
.Vector
; 
  35 import android
.annotation
.TargetApi
; 
  36 import android
.content
.Context
; 
  37 import android
.os
.Build
; 
  38 import android
.text
.format
.DateUtils
; 
  39 import android
.webkit
.MimeTypeMap
; 
  41 import com
.owncloud
.android
.MainApp
; 
  42 import com
.owncloud
.android
.R
; 
  43 import com
.owncloud
.android
.datamodel
.OCFile
; 
  46  * A helper class for some string operations. 
  48 public class DisplayUtils 
{ 
  50     private static final String OWNCLOUD_APP_NAME 
= "ownCloud"; 
  52     //private static String TAG = DisplayUtils.class.getSimpleName();  
  54     private static final String
[] sizeSuffixes 
= { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; 
  56     private static HashMap
<String
, String
> mimeType2HUmanReadable
; 
  58         mimeType2HUmanReadable 
= new HashMap
<String
, String
>(); 
  60         mimeType2HUmanReadable
.put("image/jpeg", "JPEG image"); 
  61         mimeType2HUmanReadable
.put("image/jpg", "JPEG image"); 
  62         mimeType2HUmanReadable
.put("image/png", "PNG image"); 
  63         mimeType2HUmanReadable
.put("image/bmp", "Bitmap image"); 
  64         mimeType2HUmanReadable
.put("image/gif", "GIF image"); 
  65         mimeType2HUmanReadable
.put("image/svg+xml", "JPEG image"); 
  66         mimeType2HUmanReadable
.put("image/tiff", "TIFF image"); 
  68         mimeType2HUmanReadable
.put("audio/mpeg", "MP3 music file"); 
  69         mimeType2HUmanReadable
.put("application/ogg", "OGG music file"); 
  73     private static final String TYPE_APPLICATION 
= "application"; 
  74     private static final String TYPE_AUDIO 
= "audio"; 
  75     private static final String TYPE_IMAGE 
= "image"; 
  76     private static final String TYPE_TXT 
= "text"; 
  77     private static final String TYPE_VIDEO 
= "video"; 
  79     private static final String SUBTYPE_PDF 
= "pdf"; 
  80     private static final String SUBTYPE_XML 
= "xml"; 
  81     private static final String
[] SUBTYPES_DOCUMENT 
= {  
  83         "vnd.openxmlformats-officedocument.wordprocessingml.document", 
  84         "vnd.oasis.opendocument.text", 
  88     private static Set
<String
> SUBTYPES_DOCUMENT_SET 
= new HashSet
<String
>(Arrays
.asList(SUBTYPES_DOCUMENT
)); 
  89     private static final String
[] SUBTYPES_SPREADSHEET 
= { 
  92         "vnd.openxmlformats-officedocument.spreadsheetml.sheet", 
  93         "vnd.oasis.opendocument.spreadsheet" 
  95     private static Set
<String
> SUBTYPES_SPREADSHEET_SET 
= new HashSet
<String
>(Arrays
.asList(SUBTYPES_SPREADSHEET
)); 
  96     private static final String
[] SUBTYPES_PRESENTATION 
= {  
  99         "vnd.openxmlformats-officedocument.presentationml.presentation", 
 100         "vnd.oasis.opendocument.presentation" 
 102     private static Set
<String
> SUBTYPES_PRESENTATION_SET 
= new HashSet
<String
>(Arrays
.asList(SUBTYPES_PRESENTATION
)); 
 103     private static final String
[] SUBTYPES_COMPRESSED 
= {"x-tar", "x-gzip", "zip"}; 
 104     private static final Set
<String
> SUBTYPES_COMPRESSED_SET 
= new HashSet
<String
>(Arrays
.asList(SUBTYPES_COMPRESSED
)); 
 105     private static final String SUBTYPE_OCTET_STREAM 
= "octet-stream"; 
 106     private static final String EXTENSION_RAR 
= "rar"; 
 107     private static final String EXTENSION_RTF 
= "rtf"; 
 108     private static final String EXTENSION_3GP 
= "3gp"; 
 109     private static final String EXTENSION_PY 
= "py"; 
 110     private static final String EXTENSION_JS 
= "js"; 
 113      * Converts the file size in bytes to human readable output. 
 115      * @param bytes Input file size 
 116      * @return Like something readable like "12 MB" 
 118     public static String 
bytesToHumanReadable(long bytes
) { 
 119         double result 
= bytes
; 
 120         int attachedsuff 
= 0; 
 121         while (result 
> 1024 && attachedsuff 
< sizeSuffixes
.length
) { 
 125         result 
= ((int) (result 
* 100)) / 100.; 
 126         return result 
+ " " + sizeSuffixes
[attachedsuff
]; 
 130      * Converts MIME types like "image/jpg" to more end user friendly output 
 133      * @param mimetype MIME type to convert 
 134      * @return A human friendly version of the MIME type 
 136     public static String 
convertMIMEtoPrettyPrint(String mimetype
) { 
 137         if (mimeType2HUmanReadable
.containsKey(mimetype
)) { 
 138             return mimeType2HUmanReadable
.get(mimetype
); 
 140         if (mimetype
.split("/").length 
>= 2) 
 141             return mimetype
.split("/")[1].toUpperCase() + " file"; 
 142         return "Unknown type"; 
 147      * Returns the resource identifier of an image to use as icon associated to a known MIME type. 
 149      * @param mimetype      MIME type string; if NULL, the method tries to guess it from the extension in filename 
 150      * @param filename      Name, with extension. 
 151      * @return              Identifier of an image resource. 
 153     public static int getFileTypeIconId(String mimetype
, String filename
) { 
 155         if (mimetype 
== null
) { 
 156             String fileExtension 
= getExtension(filename
); 
 157             mimetype 
= MimeTypeMap
.getSingleton().getMimeTypeFromExtension(fileExtension
); 
 158             if (mimetype 
== null
) { 
 159                 mimetype 
= TYPE_APPLICATION 
+ "/" + SUBTYPE_OCTET_STREAM
; 
 163         if ("DIR".equals(mimetype
)) { 
 164             return R
.drawable
.ic_menu_archive
; 
 167             String 
[] parts 
= mimetype
.split("/"); 
 168             String type 
= parts
[0]; 
 169             String subtype 
= (parts
.length 
> 1) ? parts
[1] : ""; 
 171             if(TYPE_TXT
.equals(type
)) { 
 172                 return R
.drawable
.file_doc
; 
 174             } else if(TYPE_IMAGE
.equals(type
)) { 
 175                 return R
.drawable
.file_image
; 
 177             } else if(TYPE_VIDEO
.equals(type
)) { 
 178                 return R
.drawable
.file_movie
; 
 180             } else if(TYPE_AUDIO
.equals(type
)) {   
 181                 return R
.drawable
.file_sound
; 
 183             } else if(TYPE_APPLICATION
.equals(type
)) { 
 185                 if (SUBTYPE_PDF
.equals(subtype
)) { 
 186                     return R
.drawable
.file_pdf
; 
 188                 } else if (SUBTYPE_XML
.equals(subtype
)) { 
 189                     return R
.drawable
.file_doc
; 
 191                 } else if (SUBTYPES_DOCUMENT_SET
.contains(subtype
)) { 
 192                     return R
.drawable
.file_doc
; 
 194                 } else if (SUBTYPES_SPREADSHEET_SET
.contains(subtype
)) { 
 195                     return R
.drawable
.file_xls
; 
 197                 } else if (SUBTYPES_PRESENTATION_SET
.contains(subtype
)) { 
 198                     return R
.drawable
.file_ppt
; 
 200                 } else if (SUBTYPES_COMPRESSED_SET
.contains(subtype
)) { 
 201                     return R
.drawable
.file_zip
; 
 203                 } else if (SUBTYPE_OCTET_STREAM
.equals(subtype
) ) { 
 204                     if (getExtension(filename
).equalsIgnoreCase(EXTENSION_RAR
)) { 
 205                         return R
.drawable
.file_zip
; 
 207                     } else if (getExtension(filename
).equalsIgnoreCase(EXTENSION_RTF
)) { 
 208                         return R
.drawable
.file_doc
; 
 210                     } else if (getExtension(filename
).equalsIgnoreCase(EXTENSION_3GP
)) { 
 211                         return R
.drawable
.file_movie
; 
 213                     } else if ( getExtension(filename
).equalsIgnoreCase(EXTENSION_PY
) || 
 214                                 getExtension(filename
).equalsIgnoreCase(EXTENSION_JS
)) { 
 215                         return R
.drawable
.file_doc
; 
 222         return R
.drawable
.file
; 
 226     private static String 
getExtension(String filename
) { 
 227         String extension 
= filename
.substring(filename
.lastIndexOf(".") + 1).toLowerCase(); 
 232      * Converts Unix time to human readable format 
 233      * @param milliseconds that have passed since 01/01/1970 
 234      * @return The human readable time for the users locale 
 236     public static String 
unixTimeToHumanReadable(long milliseconds
) { 
 237         Date date 
= new Date(milliseconds
); 
 238         DateFormat df 
= DateFormat
.getDateTimeInstance(); 
 239         return df
.format(date
); 
 243     public static int getSeasonalIconId() { 
 244         if (Calendar
.getInstance().get(Calendar
.DAY_OF_YEAR
) >= 354 && 
 245                 MainApp
.getAppContext().getString(R
.string
.app_name
).equals(OWNCLOUD_APP_NAME
)) { 
 246             return R
.drawable
.winter_holidays_icon
; 
 248             return R
.drawable
.icon
; 
 253      * Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode. 
 254      * @param url the URL where the domain name should be converted 
 255      * @param toASCII if true converts from Unicode to ASCII, if false converts from ASCII to Unicode 
 256      * @return the URL containing the converted domain name 
 258     @TargetApi(Build
.VERSION_CODES
.GINGERBREAD
) 
 259     public static String 
convertIdn(String url
, boolean toASCII
) { 
 261         if (Build
.VERSION
.SDK_INT 
>= Build
.VERSION_CODES
.GINGERBREAD
) { 
 262             // Find host name after '//' or '@' 
 264             if  (url
.indexOf("//") != -1) { 
 265                 hostStart 
= url
.indexOf("//") + "//".length(); 
 266             } else if (url
.indexOf("@") != -1) { 
 267                 hostStart 
= url
.indexOf("@") + "@".length(); 
 270             int hostEnd 
= url
.substring(hostStart
).indexOf("/"); 
 271             // Handle URL which doesn't have a path (path is implicitly '/') 
 272             hostEnd 
= (hostEnd 
== -1 ? url
.length() : hostStart 
+ hostEnd
); 
 274             String host 
= url
.substring(hostStart
, hostEnd
); 
 275             host 
= (toASCII ? IDN
.toASCII(host
) : IDN
.toUnicode(host
)); 
 277             return url
.substring(0, hostStart
) + host 
+ url
.substring(hostEnd
); 
 284      * Get the file extension if it is on path as type "content://.../DocInfo.doc" 
 285      * @param filepath: Content Uri converted to string format 
 286      * @return String: fileExtension (type '.pdf'). Empty if no extension 
 288     public static String 
getComposedFileExtension(String filepath
) { 
 289         String fileExtension 
= ""; 
 290         String fileNameInContentUri 
= filepath
.substring(filepath
.lastIndexOf("/")); 
 292         // Check if extension is included in uri 
 293         int pos 
= fileNameInContentUri
.lastIndexOf('.'); 
 295             fileExtension 
= fileNameInContentUri
.substring(pos
); 
 297         return fileExtension
; 
 300     @SuppressWarnings("deprecation") 
 301     public static CharSequence 
getRelativeDateTimeString ( 
 302             Context c
, long time
, long minResolution
, long transitionResolution
, int flags
 
 305         CharSequence dateString 
= ""; 
 308         if (time 
> System
.currentTimeMillis()){ 
 309             return DisplayUtils
.unixTimeToHumanReadable(time
); 
 311         // < 60 seconds -> seconds ago 
 312         else if ((System
.currentTimeMillis() - time
) < 60 * 1000) { 
 313             return c
.getString(R
.string
.file_list_seconds_ago
); 
 315             // Workaround 2.x bug (see https://github.com/owncloud/android/issues/716) 
 316             if (    Build
.VERSION
.SDK_INT 
<= Build
.VERSION_CODES
.HONEYCOMB 
&&  
 317                     (System
.currentTimeMillis() - time
) > 24 * 60 * 60 * 1000   ) { 
 318                 Date date 
= new Date(time
); 
 322                 dateString 
= DateUtils
.getRelativeDateTimeString( 
 323                         c
, date
.getTime(), minResolution
, transitionResolution
, flags
 
 326                 dateString 
= DateUtils
.getRelativeDateTimeString(c
, time
, minResolution
, transitionResolution
, flags
); 
 330         return dateString
.toString().split(",")[0]; 
 334      * Update the passed path removing the last "/" if it is not the root folder 
 337     public static String 
getPathWithoutLastSlash(String path
) { 
 339         // Remove last slash from path 
 340         if (path
.length() > 1 && path
.charAt(path
.length()-1) == OCFile
.PATH_SEPARATOR
.charAt(0)) { 
 341             path 
= path
.substring(0, path
.length()-1);