Java tutorial
/* * Copyright (C) 2003-2014 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.text.DecimalFormat; import java.text.Normalizer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.FileEntity; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.exoplatform.R; import org.exoplatform.model.ExoFile; import org.exoplatform.singleton.AccountSetting; import org.exoplatform.singleton.DocumentHelper; import org.exoplatform.ui.WebViewActivity; import org.exoplatform.utils.CompatibleFileOpen.FileOpenRequest; import org.exoplatform.utils.CompatibleFileOpen.FileOpenRequestResult; import org.exoplatform.widget.UnreadableFileDialog; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.StatFs; import android.provider.MediaStore; import android.provider.OpenableColumns; import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback; import android.support.v4.content.ContextCompat; import android.text.Html; import android.webkit.MimeTypeMap; public class ExoDocumentUtils { private static final String LOG_TAG = "____eXo____ExoDocumentUtils____"; public static final String ALL_VIDEO_TYPE = "video/*"; public static final String ALL_AUDIO_TYPE = "audio/*"; public static final String ALL_IMAGE_TYPE = "image/*"; public static final String ALL_TEXT_TYPE = "text/*"; public static final String IMAGE_TYPE = "image"; public static final String TEXT_TYPE = "text"; public static final String VIDEO_TYPE = "video"; public static final String AUDIO_TYPE = "audio"; public static final String MSWORD_TYPE = "application/msword"; public static final String OPEN_WORD_TYPE = "application/vnd.oasis.opendocument.text"; public static final String PDF_TYPE = "application/pdf"; public static final String XLS_TYPE = "application/xls"; public static final String OPEN_XLS_TYPE = "application/vnd.oasis.opendocument.spreadsheet"; public static final String POWERPOINT_TYPE = "application/vnd.ms-powerpoint"; public static final String OPEN_POWERPOINT_TYPE = "application/vnd.oasis.opendocument.presentation"; public static final String[] FORBIDDEN_TYPES = new String[] { "application/octet-stream" }; public static boolean isEnoughMemory(int fileSize) { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { int freeSpace = getFreeMemory(Environment.getExternalStorageDirectory().getAbsolutePath()); if (freeSpace > fileSize) { return true; } else return false; } else return false; } /* * Get free memory from path */ public static int getFreeMemory(String path) { StatFs statFs = new StatFs(path); int free = (statFs.getAvailableBlocks() * statFs.getBlockSize()); return Math.abs(free); } /* * Display file size by string decimal format */ public static String getFileSize(long fileSize) { int freeUnit; for (freeUnit = 0; fileSize >= 100; freeUnit++) { fileSize /= 1024; } DecimalFormat decFormat = new DecimalFormat("0.0"); String doubleString = decFormat.format(fileSize); StringBuffer buffer = new StringBuffer(); buffer.append(doubleString); switch (freeUnit) { case 0: buffer.append("B"); break; case 1: buffer.append("KB"); break; case 2: buffer.append("MB"); break; case 3: buffer.append("GB"); break; case 4: buffer.append("TB"); break; default: buffer.append("err"); break; } return buffer.toString(); } /* * If content type is image or text, open it in WebView else open in other * installed application */ public static FileOpenRequest fileOpen(Context context, String fileType, String filePath, String fileName) { FileOpenRequest result = new FileOpenRequest(); if (fileType == null) { new UnreadableFileDialog(context, null).show(); result.mResult = FileOpenRequestResult.ERROR; } else if (fileType.startsWith(IMAGE_TYPE) || fileType.startsWith(TEXT_TYPE)) { Intent intent = new Intent(context, WebViewActivity.class); intent.putExtra(ExoConstants.WEB_VIEW_URL, filePath); intent.putExtra(ExoConstants.WEB_VIEW_TITLE, fileName); intent.putExtra(ExoConstants.WEB_VIEW_MIME_TYPE, fileType); intent.putExtra(ExoConstants.WEB_VIEW_ALLOW_JS, "false"); context.startActivity(intent); result.mResult = FileOpenRequestResult.WEBVIEW; } else { result.mFileOpenController = new CompatibleFileOpen(context, fileType, filePath, fileName); result.mResult = FileOpenRequestResult.EXTERNAL; } if (Log.LOGD) Log.d(LOG_TAG, "File Open Result: " + result.mResult); return result; } /** * Check whether the given Mime Type is forbidden. The list of forbidden types * is in {@link ExoDocumentUtils.FORBIDDEN_TYPES} * * @param mimeType * @return true if the given Mime Type is in the list */ public static boolean isForbidden(String mimeType) { return Arrays.asList(FORBIDDEN_TYPES).contains(mimeType); } /** * Check if the device has an application to open this type of file. * * @param context Context (mandatory) * @param mimeType Mime Type to check (mandatory) * @param url file url to guess the mime type from (optional) * @return true if an application can open the given Mime Type */ public static boolean isCallable(Context context, String mimeType, String url) throws IllegalArgumentException { if (context == null || mimeType == null) throw new IllegalArgumentException("Context or mime-type cannot be null."); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setType(mimeType.toLowerCase(Locale.US)); ComponentName activity = intent.resolveActivity(context.getPackageManager()); if (activity == null && url != null) { // Fallback on a guessed mime type if the first one doesn't work String guessedMimeType = URLConnection.guessContentTypeFromName(url); if (guessedMimeType != null) { intent = new Intent(Intent.ACTION_VIEW); intent.setType(guessedMimeType.toLowerCase(Locale.US)); activity = intent.resolveActivity(context.getPackageManager()); } } return activity != null; } public static String getFullFileType(String fileType) { String docFileType = fileType; if (fileType.startsWith(ExoDocumentUtils.AUDIO_TYPE)) { docFileType = ExoDocumentUtils.ALL_AUDIO_TYPE; } else if (fileType.startsWith(ExoDocumentUtils.VIDEO_TYPE)) { docFileType = ExoDocumentUtils.ALL_VIDEO_TYPE; } else if (fileType.startsWith(ExoDocumentUtils.IMAGE_TYPE)) { docFileType = ExoDocumentUtils.ALL_IMAGE_TYPE; } else if (fileType.startsWith(ExoDocumentUtils.TEXT_TYPE)) { docFileType = ExoDocumentUtils.ALL_TEXT_TYPE; } return docFileType; } public static boolean putFileToServerFromLocal(String url, File fileManager, String fileType) { try { url = url.replaceAll(" ", "%20"); HttpPut put = new HttpPut(url); FileEntity fileEntity = new FileEntity(fileManager, fileType); put.setEntity(fileEntity); fileEntity.setContentType(fileType); HttpResponse response = ExoConnectionUtils.httpClient.execute(put); int status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else { return false; } } catch (IOException e) { if (Log.LOGD) Log.d(ExoDocumentUtils.class.getSimpleName(), e.getMessage(), Log.getStackTraceString(e)); return false; } finally { fileManager.delete(); } } public static void setRepositoryHomeUrl(String userName, String userHomeNodePath, String domain) { String documentPath = getDocumenPath(); StringBuilder buffer = new StringBuilder(); buffer.append(domain); buffer.append(documentPath); buffer.append(userHomeNodePath); try { WebdavMethod copy = new WebdavMethod("HEAD", buffer.toString()); int status = ExoConnectionUtils.httpClient.execute(copy).getStatusLine().getStatusCode(); if (status >= 200 && status < 300) { DocumentHelper.getInstance().setRepositoryHomeUrl(buffer.toString()); } else { buffer = new StringBuilder(domain); buffer.append(documentPath); buffer.append("/"); buffer.append(userName); DocumentHelper.getInstance().setRepositoryHomeUrl(buffer.toString()); } } catch (Exception e) { // XXX cannot replace because WebdavMethod, httpclient.execute can throw // exception Log.e(LOG_TAG, e.getMessage(), Log.getStackTraceString(e)); DocumentHelper.getInstance().setRepositoryHomeUrl(null); } } /** * Get the content (files and folders) of the given folder. * * @param context * @param file the folder to get content from * @return an ExoFile corresponding to the parent folder with its children * ExoFile * @throws IOException */ public static ExoFile getPersonalDriveContent(Context context, ExoFile file) throws IOException { SharedPreferences prefs = context.getSharedPreferences(ExoConstants.EXO_PREFERENCE, 0); boolean isShowHidden = prefs.getBoolean(AccountSetting.getInstance().documentKey, true); ExoFile folder = file; String domain = AccountSetting.getInstance().getDomainName(); HttpResponse response = null; String urlStr = null; /* * Put the current folder and its child list to mapping dictionary */ if (DocumentHelper.getInstance().folderToChildrenMap == null) { DocumentHelper.getInstance().folderToChildrenMap = new Bundle(); } // We're on the initial screen => list all drives if ("".equals(file.name) && "".equals(file.path)) { // personal drive ArrayList<ExoFile> arrFilesTmp = new ArrayList<ExoFile>(); ArrayList<ExoFile> fileList = new ArrayList<ExoFile>(); StringBuffer buffer = new StringBuffer(); buffer.append(domain); buffer.append(ExoConstants.DOCUMENT_DRIVE_PATH_REST); buffer.append(ExoConstants.DOCUMENT_PERSONAL_DRIVER); buffer.append(ExoConstants.DOCUMENT_PERSONAL_DRIVER_SHOW_PRIVATE); buffer.append(isShowHidden); urlStr = buffer.toString(); response = ExoConnectionUtils.getRequestResponse(urlStr); fileList = getDrives(response); if (fileList != null && fileList.size() > 0) { arrFilesTmp.add(new ExoFile(ExoConstants.DOCUMENT_PERSONAL_DRIVER)); arrFilesTmp.addAll(fileList); } // general drive buffer = new StringBuffer(); buffer.append(domain); buffer.append(ExoConstants.DOCUMENT_DRIVE_PATH_REST); buffer.append(ExoConstants.DOCUMENT_GENERAL_DRIVER); urlStr = buffer.toString(); response = ExoConnectionUtils.getRequestResponse(urlStr); fileList = getDrives(response); if (fileList != null && fileList.size() > 0) { arrFilesTmp.add(new ExoFile(ExoConstants.DOCUMENT_GENERAL_DRIVER)); arrFilesTmp.addAll(fileList); } // group drive buffer = new StringBuffer(); buffer.append(domain); buffer.append(ExoConstants.DOCUMENT_DRIVE_PATH_REST); buffer.append(ExoConstants.DOCUMENT_GROUP_DRIVER); urlStr = buffer.toString(); response = ExoConnectionUtils.getRequestResponse(urlStr); // "true" to generate the natural name for the folders in the group fileList = getDrives(response, true); if (fileList != null && fileList.size() > 0) { arrFilesTmp.add(new ExoFile(ExoConstants.DOCUMENT_GROUP_DRIVER)); arrFilesTmp.addAll(fileList); } // store the drives root folders if (DocumentHelper.getInstance().folderToChildrenMap.containsKey(ExoConstants.DOCUMENT_JCR_PATH)) { DocumentHelper.getInstance().folderToChildrenMap.remove(ExoConstants.DOCUMENT_JCR_PATH); DocumentHelper.getInstance().folderToChildrenMap .putParcelableArrayList(ExoConstants.DOCUMENT_JCR_PATH, arrFilesTmp); } else { DocumentHelper.getInstance().folderToChildrenMap .putParcelableArrayList(ExoConstants.DOCUMENT_JCR_PATH, arrFilesTmp); } // create an empty root folder to hold all the drives folder.children = arrFilesTmp; } else { // We're in a drive or folder => list its content urlStr = getDriverUrl(file); urlStr = ExoUtils.encodeDocumentUrl(urlStr); response = ExoConnectionUtils.getRequestResponse(urlStr); folder = getContentOfFolder(response, file); // store the children of the loaded folder if (DocumentHelper.getInstance().folderToChildrenMap.containsKey(file.path)) { DocumentHelper.getInstance().folderToChildrenMap.remove(file.path); DocumentHelper.getInstance().folderToChildrenMap.putParcelableArrayList(file.path, new ArrayList<ExoFile>(folder.children)); } else DocumentHelper.getInstance().folderToChildrenMap.putParcelableArrayList(file.path, new ArrayList<ExoFile>(folder.children)); } return folder; } public static String fullURLofFile(String workSpaceName, String url) { String domain = AccountSetting.getInstance().getDomainName(); StringBuffer buffer = new StringBuffer(domain); buffer.append(ExoConstants.DOCUMENT_JCR_PATH); buffer.append("/"); buffer.append(DocumentHelper.getInstance().repository); buffer.append("/"); buffer.append(workSpaceName); buffer.append(url); return buffer.toString(); } private static String getDocumenPath() { StringBuilder documentPath = new StringBuilder(); documentPath.append(ExoConstants.DOCUMENT_JCR_PATH); documentPath.append("/"); documentPath.append(DocumentHelper.getInstance().repository); documentPath.append("/"); documentPath.append(ExoConstants.DOCUMENT_COLLABORATION); return documentPath.toString(); } /** * Get the list of folders in a drive, from the HTTP response.<br/> * The response's body is an XM document that is parsed to extract the * information of the folders. <br/> * This method simply calls * * <pre> * ExoDocumentUtils.getDrives(response, false); * </pre> * * @param response the HttpResponse from where to extract the list of folders * @return an ArrayList of ExoFile or an empty ArrayList if a problem happens * @see ExoDocumentUtils.getDrives(HttpResponse response, boolean * isGroupDrive) */ public static ArrayList<ExoFile> getDrives(HttpResponse response) { return getDrives(response, false); } /** * Get the list of folders in a drive, from the HTTP response.<br/> * The response's body is an XM document that is parsed to extract the * information of the folders. <br/> * If <i>isGroupDrive = true</i> , each folder's name is improved to be less * technical, by calling <i>ExoFile#createNaturalName()</i> * * @param response the HttpResponse from where to extract the list of folders * @param isGroupDrive if <i>true</i> the file's natural name will be created * @return an ArrayList of ExoFile or an empty ArrayList if a problem happens */ public static ArrayList<ExoFile> getDrives(HttpResponse response, boolean isGroupDrive) { // Initialize the blogEntries MutableArray that we declared in the // header ArrayList<ExoFile> folderArray = new ArrayList<ExoFile>(); try { Document obj_doc = null; DocumentBuilderFactory doc_build_fact = null; DocumentBuilder doc_builder = null; doc_build_fact = DocumentBuilderFactory.newInstance(); doc_builder = doc_build_fact.newDocumentBuilder(); InputStream is = ExoConnectionUtils.sendRequest(response); if (is != null) { obj_doc = doc_builder.parse(is); NodeList obj_nod_list = null; if (null != obj_doc) { obj_nod_list = obj_doc.getElementsByTagName("Folder"); for (int i = 0; i < obj_nod_list.getLength(); i++) { Node itemNode = obj_nod_list.item(i); if (itemNode.getNodeType() == Node.ELEMENT_NODE) { Element itemElement = (Element) itemNode; ExoFile file = new ExoFile(); file.name = itemElement.getAttribute("name"); // if (Config.GD_INFO_LOGS_ENABLED) Log.i(" Public file name", file.name); file.workspaceName = itemElement.getAttribute("workspaceName"); file.driveName = file.name; file.currentFolder = itemElement.getAttribute("currentFolder"); if (file.currentFolder == null) file.currentFolder = ""; file.isFolder = true; // create the folder's natural name only for folders // in the group drive if (isGroupDrive) file.createNaturalName(); /* * If file name is "Public", get path for it. */ if (file.name.equals("Public")) { file.path = getRootDriverPath(file); } folderArray.add(file); } } } } } catch (ParserConfigurationException e) { Log.e(" ParserConfigurationException ", e.getMessage()); folderArray = null; } catch (SAXException e) { Log.e(" SAXException ", e.getMessage()); folderArray = null; } catch (IOException e) { Log.e(" IOException ", e.getMessage()); folderArray = null; } return folderArray; } // return the driver url private static String getDriverUrl(ExoFile file) { String domain = AccountSetting.getInstance().getDomainName(); StringBuffer buffer = new StringBuffer(domain); buffer.append(ExoConstants.DOCUMENT_FILE_PATH_REST); buffer.append(file.driveName); buffer.append(ExoConstants.DOCUMENT_WORKSPACE_NAME); buffer.append(file.workspaceName); buffer.append(ExoConstants.DOCUMENT_CURRENT_FOLDER); buffer.append(file.currentFolder); return buffer.toString(); } // get path for driver folder (ex. Public/Private) private static String getRootDriverPath(ExoFile file) { String path = null; String urlStr = getDriverUrl(file); urlStr = ExoUtils.encodeDocumentUrl(urlStr); Document obj_doc = null; DocumentBuilderFactory doc_build_fact = null; DocumentBuilder doc_builder = null; try { HttpResponse response = ExoConnectionUtils.getRequestResponse(urlStr); doc_build_fact = DocumentBuilderFactory.newInstance(); doc_builder = doc_build_fact.newDocumentBuilder(); InputStream is = ExoConnectionUtils.sendRequest(response); if (is != null) { obj_doc = doc_builder.parse(is); if (null != obj_doc) { // Get folders NodeList obj_nod_list = obj_doc.getElementsByTagName("Folder"); Node rootNode = obj_nod_list.item(0); if (rootNode.getNodeType() == Node.ELEMENT_NODE) { Element itemElement = (Element) rootNode; path = fullURLofFile(ExoConstants.DOCUMENT_COLLABORATION, itemElement.getAttribute("path")); } } } return path; } catch (ParserConfigurationException e) { return null; } catch (SAXException e) { return null; } catch (IOException e) { return null; } } private static ExoFile getFileFromXMLElement(Element element, boolean isFolder) throws NullPointerException { if (element == null) throw new NullPointerException("Given element is null"); ExoFile file = new ExoFile(); if (element.hasAttribute("title")) { file.name = Html.fromHtml(element.getAttribute("title")).toString(); } else { file.name = element.getAttribute("name"); } file.workspaceName = element.getAttribute("workspaceName"); file.path = fullURLofFile(file.workspaceName, element.getAttribute("path")); if (element.hasAttribute("driveName")) file.driveName = element.getAttribute("driveName"); else file.driveName = file.name; file.currentFolder = element.getAttribute("currentFolder"); if (file.currentFolder == null) file.currentFolder = ""; file.isFolder = isFolder; if (element.hasAttribute("nodeType")) file.nodeType = element.getAttribute("nodeType"); String canRemove = element.getAttribute("canRemove"); file.canRemove = Boolean.parseBoolean(canRemove.trim()); return file; } /** * Get a folder with its sub-files and sub-folders.<br/> * Parse the XML response with format: * * <pre> * <Folder canAddChild="bool" canRemove="bool" currentFolder="Name" driveName="DriveName" hasChild="bool" name="Name" nodeType="nt" path="..." title="Title" titlePath="Title" workspaceName="Name"> * <Folders> * <Folder canAddChild="bool" canRemove="bool" currentFolder="Name" driveName="DriveName" hasChild="bool" name="Name" nodeType="nt" path="..." title="Title" titlePath="Title" workspaceName="Name"/> * </Folders> * <Files> * <File canRemove="bool" creator="username" dateCreated="Date" dateModified="Date" name="doc.jpg" nodeType="nt" path="..." size="0" title="doc.jpg" workspaceName="Name"/> * </Files> * </Folder> * </pre> * * Example URL: * * <pre> * https://SERVER/rest/managedocument/getFoldersAndFiles?driveName=Personal% * 20Documents&workspaceName=collaboration¤tFolder=Public * </pre> * * @param response the response that contains the XML entity * @param file The folder to retrieve the content from * @return an ExoFile that represents the content of the given folder */ public static ExoFile getContentOfFolder(HttpResponse response, ExoFile file) { ExoFile folder = file; ArrayList<ExoFile> childrenArray = new ArrayList<ExoFile>(); Document obj_doc = null; DocumentBuilderFactory doc_build_fact = null; DocumentBuilder doc_builder = null; try { doc_build_fact = DocumentBuilderFactory.newInstance(); doc_builder = doc_build_fact.newDocumentBuilder(); InputStream is = ExoConnectionUtils.sendRequest(response); if (is != null) { obj_doc = doc_builder.parse(is); NodeList obj_nod_list = null; if (null != obj_doc) { // Get folders obj_nod_list = obj_doc.getElementsByTagName("Folder"); for (int i = 0; i < obj_nod_list.getLength(); i++) { Node itemNode = obj_nod_list.item(i); if (itemNode.getNodeType() == Node.ELEMENT_NODE) { Element itemElement = (Element) itemNode; if (i == 0) { // The first element is always the root folder // We copy properties from tmp to folder // to keep the pointer to the folder instance ExoFile tmp = getFileFromXMLElement(itemElement, true); // Unfortunate hack // The drive "Personal Documents" is a folder named "Private" // We do this to display the drive name in this case if ("Private".equals(tmp.name) && "".equals(tmp.currentFolder) && "Personal Documents".equals(tmp.driveName)) folder.name = tmp.driveName; else folder.name = tmp.name; folder.workspaceName = tmp.workspaceName; folder.path = tmp.path; folder.driveName = tmp.driveName; folder.currentFolder = tmp.currentFolder; folder.isFolder = true; // Cannot delete the root folder of a drive if (folder.isFolder && "".equals(folder.currentFolder)) folder.canRemove = false; else folder.canRemove = tmp.canRemove; } else { // Folders of the root folder ExoFile childFolder = getFileFromXMLElement(itemElement, true); childrenArray.add(childFolder); } } } // Get files obj_nod_list = obj_doc.getElementsByTagName("File"); for (int i = 0; i < obj_nod_list.getLength(); i++) { Node itemNode = obj_nod_list.item(i); if (itemNode.getNodeType() == Node.ELEMENT_NODE) { Element itemElement = (Element) itemNode; ExoFile childFile = getFileFromXMLElement(itemElement, false); childrenArray.add(childFile); } } } } folder.children = childrenArray; return folder; } catch (ParserConfigurationException e) { return null; } catch (SAXException e) { return null; } catch (IOException e) { return null; } } /* * Get document icon from content type */ public static int getIconFromType(String contentType) { int id = R.drawable.documenticonforunknown; if (contentType != null) { if (contentType.indexOf(IMAGE_TYPE) >= 0) id = R.drawable.documenticonforimage; else if (contentType.indexOf(VIDEO_TYPE) >= 0) id = R.drawable.documenticonforvideo; else if (contentType.indexOf(AUDIO_TYPE) >= 0) id = R.drawable.documenticonformusic; else if (contentType.indexOf(MSWORD_TYPE) >= 0 || contentType.indexOf(OPEN_WORD_TYPE) >= 0) id = R.drawable.documenticonforword; else if (contentType.indexOf(PDF_TYPE) >= 0) id = R.drawable.documenticonforpdf; else if (contentType.indexOf(XLS_TYPE) >= 0 || contentType.indexOf(OPEN_XLS_TYPE) >= 0) id = R.drawable.documenticonforxls; else if (contentType.indexOf(POWERPOINT_TYPE) >= 0 || contentType.indexOf(OPEN_POWERPOINT_TYPE) >= 0) id = R.drawable.documenticonforppt; else if (contentType.indexOf(TEXT_TYPE) >= 0) id = R.drawable.documenticonfortxt; } return id; } public static String getParentUrl(String url) { int index = url.lastIndexOf("/"); if (index > 0) return url.substring(0, index); return ""; } public static String getLastPathComponent(String url) { int index = url.lastIndexOf("/"); if (index > 0) return url.substring(url.lastIndexOf("/") + 1, url.length()); return url; } public static boolean isContainSpecialChar(String str, String charSet) { Pattern patt = Pattern.compile(charSet); Matcher matcher = patt.matcher(str); return matcher.find(); } /** * Delete remote folder or file * * @param url the URL of the folder or file to delete * @return true if the file was deleted, false otherwise */ public static boolean deleteFile(String url) { HttpResponse response; try { url = ExoUtils.encodeDocumentUrl(url); WebdavMethod delete = new WebdavMethod("DELETE", url); response = ExoConnectionUtils.httpClient.execute(delete); int status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else return false; } catch (IOException e) { return false; } } // Copy file/folder method public static boolean copyFile(String source, String destination) { HttpResponse response; try { if (source.equals(destination)) { return false; } source = ExoUtils.encodeDocumentUrl(source); destination = ExoUtils.encodeDocumentUrl(destination); WebdavMethod copy = new WebdavMethod("COPY", source, destination); response = ExoConnectionUtils.httpClient.execute(copy); int status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else return false; } catch (IOException e) { if (Log.LOGD) Log.d(ExoDocumentUtils.class.getSimpleName(), e.getMessage(), Log.getStackTraceString(e)); return false; } } // Move file/folder method public static boolean moveFile(String source, String destination) { HttpResponse response; try { if (source.equals(destination)) { return false; } source = ExoUtils.encodeDocumentUrl(source); destination = ExoUtils.encodeDocumentUrl(destination); WebdavMethod move = new WebdavMethod("MOVE", source, destination); response = ExoConnectionUtils.httpClient.execute(move); int status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else return false; } catch (IOException e) { return false; } } public static boolean renameFolder(String source, String destination) { HttpResponse response; try { source = ExoUtils.encodeDocumentUrl(source); destination = ExoUtils.encodeDocumentUrl(destination); WebdavMethod create = new WebdavMethod("HEAD", destination); response = ExoConnectionUtils.httpClient.execute(create); int status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else { WebdavMethod move = new WebdavMethod("MOVE", source, destination); response = ExoConnectionUtils.httpClient.execute(move); status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else { return false; } } } catch (IOException e) { return false; } } public static boolean createFolder(String destination) { HttpResponse response; try { destination = ExoUtils.encodeDocumentUrl(destination); WebdavMethod create = new WebdavMethod("HEAD", destination); response = ExoConnectionUtils.httpClient.execute(create); int status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else { create = new WebdavMethod("MKCOL", destination); response = ExoConnectionUtils.httpClient.execute(create); status = response.getStatusLine().getStatusCode(); if (status >= HttpStatus.SC_OK && status < HttpStatus.SC_MULTIPLE_CHOICES) { return true; } else return false; } } catch (Exception e) { // XXX catch null of destination, WebdavMethod initial, httpclient // exception Log.e(LOG_TAG, e.getMessage(), e); return false; } } /** * Returns a DocumentInfo with info coming from the file at the given URI * * @param document the URI of a file or a content * @param context * @return a DocumentInfo or null if an error occurs */ public static DocumentInfo documentInfoFromUri(Uri document, Context context) { if (document == null) return null; if (document.toString().startsWith("content://")) { /* * Some apps send fake content:// URI with real file:// URI inside * E.g. open ASTRO File Manager > View File > Share : * * content://authority/-1/1/file:///sdcard/path/file.jpg/ACTUAL/123 * * Then we extract the real URI and pass it to documentFromFileUri(...) */ String decodedUri = Uri.decode(document.toString()); int fileIdx = decodedUri.indexOf("file://"); if (fileIdx > -1) { long id = -1; try { id = ContentUris.parseId(document); } catch (NumberFormatException e) { Log.e(LOG_TAG, e.getMessage(), e); } catch (UnsupportedOperationException e) { Log.e(LOG_TAG, e.getMessage(), e); } String fileUri = decodedUri.substring(fileIdx); fileUri = fileUri.replaceAll("(/ACTUAL/)(" + id + ")", ""); return documentFromFileUri(Uri.parse(fileUri)); } else { return documentFromContentUri(document, context); } } else if (document.toString().startsWith("file://")) { return documentFromFileUri(document); } else { return null; // other formats not supported } } /** * Gets a DocumentInfo with info coming from the document at the given URI. * * @param contentUri the content URI of the document (content:// ...) * @param context * @return a DocumentInfo or null if an error occurs */ public static DocumentInfo documentFromContentUri(Uri contentUri, Context context) { if (contentUri == null) return null; try { ContentResolver cr = context.getContentResolver(); Cursor c = cr.query(contentUri, null, null, null, null); int sizeIndex = c.getColumnIndex(OpenableColumns.SIZE); int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME); int orientIndex = c.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION); c.moveToFirst(); DocumentInfo document = new DocumentInfo(); document.documentName = c.getString(nameIndex); document.documentSizeKb = c.getLong(sizeIndex) / 1024; document.documentData = cr.openInputStream(contentUri); document.documentMimeType = cr.getType(contentUri); if (orientIndex != -1) { // if found orientation column document.orientationAngle = c.getInt(orientIndex); } return document; } catch (FileNotFoundException e) { Log.d(LOG_TAG, e.getClass().getSimpleName(), e.getLocalizedMessage()); } catch (Exception e) { Log.e(LOG_TAG, "Cannot retrieve the content at " + contentUri); if (Log.LOGD) Log.d(LOG_TAG, e.getMessage() + "\n" + Log.getStackTraceString(e)); } return null; } /** * Gets a DocumentInfo with info coming from the file at the given URI. * * @param fileUri the file URI (file:// ...) * @return a DocumentInfo or null if an error occurs */ public static DocumentInfo documentFromFileUri(Uri fileUri) { if (fileUri == null) return null; try { URI uri = new URI(fileUri.toString()); File file = new File(uri); DocumentInfo document = new DocumentInfo(); document.documentName = file.getName(); document.documentSizeKb = file.length() / 1024; document.documentData = new FileInputStream(file); // Guess the mime type in 2 ways try { // 1) by inspecting the file's first bytes document.documentMimeType = URLConnection.guessContentTypeFromStream(document.documentData); } catch (IOException e) { document.documentMimeType = null; } if (document.documentMimeType == null) { // 2) if it fails, by stripping the extension of the filename // and getting the mime type from it String extension = ""; int dotPos = document.documentName.lastIndexOf('.'); if (0 <= dotPos) extension = document.documentName.substring(dotPos + 1); document.documentMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); } // Get the orientation angle from the EXIF properties if ("image/jpeg".equals(document.documentMimeType)) document.orientationAngle = getExifOrientationAngleFromFile(file.getAbsolutePath()); return document; } catch (URISyntaxException e) { Log.e(LOG_TAG, "Cannot retrieve the file at " + fileUri); if (Log.LOGD) Log.d(LOG_TAG, e.getMessage() + "\n" + Log.getStackTraceString(e)); } catch (FileNotFoundException e) { Log.e(LOG_TAG, "Cannot retrieve the file at " + fileUri); if (Log.LOGD) Log.d(LOG_TAG, e.getMessage() + "\n" + Log.getStackTraceString(e)); } return null; } /** * Delete the Files at the given paths * * @param files a list of file paths * @return true if all files were deleted, false otherwise */ public static boolean deleteLocalFiles(List<String> files) { boolean result = true; if (files != null) { for (String filePath : files) { File f = new File(filePath); boolean del = f.delete(); Log.d(LOG_TAG, "File " + f.getName() + " deleted: " + (del ? "YES" : "NO")); result &= del; } } return result; } /** * On Platform 4.1-M2, the upload service renames the uploaded file. Therefore * the link to this file in the activity becomes incorrect. To fix this, we * rename the file before upload so the same name is used in the activity. * * @param originalName the name to clean * @return a String without forbidden characters */ public static String cleanupFilename(String originalName) { final String TILDE_HYPHENS_COLONS_SPACES = "[~_:\\s]"; final String MULTIPLE_HYPHENS = "-{2,}"; final String FORBIDDEN_CHARS = "[`!@#\\$%\\^&\\*\\|;\"'<>/\\\\\\[\\]\\{\\}\\(\\)\\?,=\\+\\.]+"; String name = originalName; String ext = ""; int lastDot = name.lastIndexOf('.'); if (lastDot > 0 && lastDot < name.length()) { ext = name.substring(lastDot); // the ext with the dot name = name.substring(0, lastDot); // the name before the ext } // [~_:\s] Replaces ~ _ : and spaces by - name = Pattern.compile(TILDE_HYPHENS_COLONS_SPACES).matcher(name).replaceAll("-"); // [`!@#\$%\^&\*\|;"'<>/\\\[\]\{\}\(\)\?,=\+\.]+ Deletes forbidden chars name = Pattern.compile(FORBIDDEN_CHARS).matcher(name).replaceAll(""); // Converts accents to regular letters name = Normalizer.normalize(name, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""); // Replaces upper case characters by lower case // Locale loc = new // Locale(SettingUtils.getPrefsLanguage(getApplicationContext())); name = name.toLowerCase(Locale.getDefault()); // Remove consecutive - name = Pattern.compile(MULTIPLE_HYPHENS).matcher(name).replaceAll("-"); // Save return (name + ext); } public static final int ROTATION_0 = 0; public static final int ROTATION_90 = 90; public static final int ROTATION_180 = 180; public static final int ROTATION_270 = 270; /** * Get the EXIF orientation of the given file * * @param filePath * @return an int in ExoDocumentUtils.ROTATION_[0 , 90 , 180 , 270] */ public static int getExifOrientationAngleFromFile(String filePath) { int ret = ROTATION_0; try { ret = new ExifInterface(filePath).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); switch (ret) { case ExifInterface.ORIENTATION_ROTATE_90: ret = ROTATION_90; break; case ExifInterface.ORIENTATION_ROTATE_180: ret = ROTATION_180; break; case ExifInterface.ORIENTATION_ROTATE_270: ret = ROTATION_270; break; default: break; } } catch (IOException e) { if (Log.LOGD) Log.d(ExoDocumentUtils.class.getSimpleName(), e.getMessage(), Log.getStackTraceString(e)); } return ret; } /** * Rotate the bitmap at its correct orientation * * @param filePath the file where the bitmap is stored * @param source the bitmap itself * @return the bitmap rotated with * {@link ExoDocumentUtils.rotateBitmapByAngle(Bitmap, int)} */ public static Bitmap rotateBitmapToNormal(String filePath, Bitmap source) { Bitmap ret = source; int orientation = getExifOrientationAngleFromFile(filePath); // Sometimes we get an orientation = 1 // To avoid a 1 rotation, // we rotate only when the orientation is exactly 90 or 180 or 270 if (orientation == ROTATION_90 || orientation == ROTATION_180 || orientation == ROTATION_270) { ret = rotateBitmapByAngle(source, orientation); } return ret; } /** * Rotate the bitmap by a certain angle. Uses {@link Matrix#postRotate(int)} * * @param source the bitmap to rotate * @param angle the rotation angle * @return a new rotated bitmap */ public static Bitmap rotateBitmapByAngle(Bitmap source, int angle) { Bitmap ret = source; int w, h; w = source.getWidth(); h = source.getHeight(); Matrix matrix = new Matrix(); matrix.postRotate(angle); try { ret = Bitmap.createBitmap(source, 0, 0, w, h, matrix, true); } catch (OutOfMemoryError e) { Log.d(ExoDocumentUtils.class.getSimpleName(), "Exception : ", e, Log.getStackTraceString(e)); } return ret; } private static String permissionForCode(int permCode) { String permission = null; switch (permCode) { case ExoConstants.REQUEST_TAKE_PICTURE_WITH_CAMERA: // We store the captured image on disk, so we need the WRITE_EXTERNAL_STORAGE permission permission = Manifest.permission.WRITE_EXTERNAL_STORAGE; break; case ExoConstants.REQUEST_PICK_IMAGE_FROM_GALLERY: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) // On Jelly Bean and after, return the actual READ_EXTERNAL_STORAGE permission permission = permissionReadExternalStorage(); else // Otherwise returning WRITE_EXTERNAL_STORAGE implicitly grants READ_EXTERNAL_STORAGE permission = Manifest.permission.WRITE_EXTERNAL_STORAGE; break; default: throw new IllegalArgumentException("Given permission code is incorrect: " + permCode); } return permission; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private static String permissionReadExternalStorage() { return Manifest.permission.READ_EXTERNAL_STORAGE; } /** * Check whether the application needs to request the permission required by the activity. * If yes, then the permission is requested (via {@link ActivityCompat#requestPermissions(Activity, String[], int)}). * * @param caller The activity that requires the permission. Must implement {@link OnRequestPermissionsResultCallback}. * @param permissionCode The code defined internally, e.g. {@link ExoConstants.REQUEST_PICK_IMAGE_FROM_GALLERY}. * @return true if the permission has been requested <br/> * false if the permission was already granted */ public static boolean didRequestPermission(Activity caller, int permissionCode) { if (caller == null || !(caller instanceof OnRequestPermissionsResultCallback)) throw new IllegalArgumentException("Caller activity must implement OnRequestPermissionsResultCallback"); boolean res = false; String permission = permissionForCode(permissionCode); int check = ContextCompat.checkSelfPermission(caller, permission); if (check != PackageManager.PERMISSION_GRANTED) { res = true; ActivityCompat.requestPermissions(caller, new String[] { permission }, permissionCode); } return res; } /** * Check whether the request for the specified permission should be explained to the user. * Calls {@link ActivityCompat#shouldShowRequestPermissionRationale(Activity, String)}. * * @param activity The activity that requires the permission. * @param permCode The code defined internally, e.g. {@link ExoConstants.REQUEST_PICK_IMAGE_FROM_GALLERY}. * @return true if the user should receive more information about the permission request */ public static boolean shouldDisplayExplanation(Activity activity, int permCode) { if (activity == null) throw new IllegalArgumentException("Caller activity must not be null"); String permission = permissionForCode(permCode); return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission); } public static class DocumentInfo { public String documentName; public long documentSizeKb; public InputStream documentData; public String documentMimeType; public int orientationAngle = ROTATION_0; @Override public String toString() { return String.format(Locale.US, "File %s [%s - %s KB]", documentName, documentMimeType, documentSizeKb); } public void closeDocStream() { if (documentData != null) try { documentData.close(); } catch (IOException e) { if (Log.LOGD) Log.d(LOG_TAG, Log.getStackTraceString(e)); } } /** * On Platform 4.1-M2, the upload service renames the uploaded file. * Therefore the link to this file in the activity becomes incorrect. To fix * this, we rename the file before upload so the same name is used in the * activity. */ public void cleanupFilename(Context context) { final String TILDE_HYPHENS_COLONS_SPACES = "[~_:\\s]"; final String MULTIPLE_HYPHENS = "-{2,}"; final String FORBIDDEN_CHARS = "[`!@#\\$%\\^&\\*\\|;\"'<>/\\\\\\[\\]\\{\\}\\(\\)\\?,=\\+\\.]+"; String name = documentName; String ext = ""; int lastDot = name.lastIndexOf('.'); if (lastDot > 0 && lastDot < name.length()) { ext = name.substring(lastDot); // the ext with the dot name = name.substring(0, lastDot); // the name before the ext } // [~_:\s] Replaces ~ _ : and spaces by - name = Pattern.compile(TILDE_HYPHENS_COLONS_SPACES).matcher(name).replaceAll("-"); // [`!@#\$%\^&\*\|;"'<>/\\\[\]\{\}\(\)\?,=\+\.]+ Deletes forbidden chars name = Pattern.compile(FORBIDDEN_CHARS).matcher(name).replaceAll(""); // Converts accents to regular letters name = Normalizer.normalize(name, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", ""); // Replaces upper case characters by lower case Locale loc = new Locale(SettingUtils.getPrefsLanguage(context.getApplicationContext())); name = name.toLowerCase(loc == null ? Locale.getDefault() : loc); // Remove consecutive - name = Pattern.compile(MULTIPLE_HYPHENS).matcher(name).replaceAll("-"); // Save documentName = name + ext; } } }