/* * Copyright 2014 Gleb Godonoga. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.andrada.sitracker.util; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Environment; import android.text.TextUtils; import com.andrada.sitracker.Constants; import com.andrada.sitracker.db.beans.Publication; import com.andrada.sitracker.exceptions.SharePublicationException; import com.github.kevinsawicki.http.HttpRequest; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.andrada.sitracker.util.LogUtils.LOGW; public final class ShareHelper { @NotNull public static Intent getSharePublicationIntent(Uri file) { Intent share = new Intent(Intent.ACTION_VIEW); share.addCategory(Intent.CATEGORY_DEFAULT); share.setDataAndType(file, "text/html"); return share; } /** * Fetches publication html file into downloadFolder or default files folder * * @param context of the App, can be null if pubFolder is not blank * @param pub the actual publication to download * @param pubFolder folder to download or to look into * @param forceDownload if the file exists * @return Intent for downloaded or existing file * @throws SharePublicationException */ public static Intent fetchPublication(Context context, @NotNull Publication pub, String pubFolder, boolean forceDownload) throws SharePublicationException { String pubUrl = pub.getUrl(); File file; if (TextUtils.isEmpty(pubFolder)) { file = ShareHelper.getPublicationStorageFile(context, pub.getAuthor().getName() + "_" + pub.getName()); } else { file = ShareHelper.getPublicationStorageFileWithPath(pubFolder, pub.getAuthor().getName() + "_" + pub.getName()); } if (file == null) { throw new SharePublicationException( SharePublicationException.SharePublicationErrors.STORAGE_NOT_ACCESSIBLE_FOR_PERSISTANCE); } if (forceDownload || !file.exists() || file.lastModified() < pub.getUpdateDate().getTime()) { try { String cgiPubUrl = Constants.SAMLIB_CGI_PUBLICAITON_URL + SamlibPageHelper.getReducedUrlFromCompletePublicationUrl(pubUrl); URL publicaitonUrl = new URL(cgiPubUrl); HttpRequest request = HttpRequest.get(publicaitonUrl); if (request.code() == 200) { BufferedReader reader = request.bufferedReader(Constants.DEFAULT_SAMLIB_ENCODING); boolean result = ShareHelper.saveHtmlPageToFile(file, reader); if (!result) { throw new SharePublicationException( SharePublicationException.SharePublicationErrors.COULD_NOT_PERSIST); } } else { throw new SharePublicationException( SharePublicationException.SharePublicationErrors.COULD_NOT_LOAD); } } catch (MalformedURLException e) { throw new SharePublicationException( SharePublicationException.SharePublicationErrors.WRONG_PUBLICATION_URL); } catch (HttpRequest.HttpRequestException e) { throw new SharePublicationException( SharePublicationException.SharePublicationErrors.COULD_NOT_LOAD); } } return getSharePublicationIntent(Uri.fromFile(file)); } public static boolean shouldRefreshPublication(Context context, @NotNull Publication pub, String pubFolder) { File file; if (TextUtils.isEmpty(pubFolder)) { file = ShareHelper.getPublicationStorageFile(context, pub.getAuthor().getName() + "_" + pub.getName()); } else { file = ShareHelper.getPublicationStorageFileWithPath(pubFolder, pub.getAuthor().getName() + "_" + pub.getName()); } return file == null || !file.exists() || file.lastModified() < pub.getUpdateDate().getTime(); } /** * Get a usable File of the publication on external storage * * @param context The context to use * @param hashedPublicationName A unique hash of the publication url * @return The file or null if storage is not accessible. */ @Nullable public static File getPublicationStorageFile(@NotNull Context context, String hashedPublicationName) { File storageDir = getPublicationStorageDirectory(context); if (storageDir == null) { return null; } return new File(storageDir, hashedPublicationName + ".html"); } @Nullable public static File getPublicationStorageFileWithPath(String path, String filename) { File storageDir = getExternalDirectoryBasedOnPath(path); if (storageDir == null) { return null; } return new File(storageDir, sanitizeFileName(filename + ".html")); } private static String sanitizeFileName(@NotNull String badFileName) { final String pattern = "[^0-9\\s_\\p{L}\\(\\)%\\-\\.]"; StringBuffer cleanFileName = new StringBuffer(); Pattern filePattern = Pattern.compile(pattern); Matcher fileMatcher = filePattern.matcher(badFileName); boolean match = fileMatcher.find(); while (match) { fileMatcher.appendReplacement(cleanFileName, ""); match = fileMatcher.find(); } fileMatcher.appendTail(cleanFileName); return cleanFileName.substring(0, cleanFileName.length() > 126 ? 126 : cleanFileName.length()); } /** * Get a the external directory name. * * @param context The context to use * @return external directory path, null if directory not available */ @Nullable public static File getPublicationStorageDirectory(@NotNull Context context) { // Check if media is mounted or storage is built-in, if so, try and use external cache dir // otherwise return null if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && Environment.isExternalStorageRemovable()) { return null; } return context.getExternalFilesDir(null); } /** * Get the external sd card directory based on the specified path. * * @param path path to try * @return File instance or null if storage is not accessible or path is invalid */ @Nullable @Contract("null -> null") public static File getExternalDirectoryBasedOnPath(@Nullable String path) { //Sanity check 1 if (path == null) { return null; } //Sanity check 2 if (path.indexOf("/") != 0) { path = "/" + path; } //Sanity check 3 if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && Environment.isExternalStorageRemovable()) { return null; } //Path here is always absolute. //File sdCard = Environment.getExternalStorageDirectory(); File dir = new File(/*sdCard.getAbsolutePath() + */path); //Make sure we create directories if they do not exist if (!dir.exists()) { dir.mkdirs(); } //Sanity check 5 if (dir.exists() && dir.isDirectory()) { return dir; } else { return null; } } @NotNull public static String getTimestampFilename(@NotNull String prefix, @NotNull String extension) { SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy"); return prefix + fmt.format(new Date()) + extension; } /** * Saves a file with specified html page content and character set. * If the page does not contain a meta Content-Type header, it is added and the files is saved as UTF-8 * * @param file The file to save to * @param reader Buffered reader of content to save * @return true if save was successful, false otherwise */ public static boolean saveHtmlPageToFile(@NotNull File file, @NotNull BufferedReader reader) throws SharePublicationException { boolean result = true; BufferedOutputStream bs = null; FileOutputStream fs = null; try { fs = new FileOutputStream(file); bs = new BufferedOutputStream(fs); String line; String charSet = "UTF-8"; line = reader.readLine(); String[] str = line.split("\\|"); if (str.length == 0) { //No information at all. Probably something is wrong - just fail throw new SharePublicationException( SharePublicationException.SharePublicationErrors.COULD_NOT_LOAD); } bs.write("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">".getBytes(charSet)); if (str.length > 1) { bs.write(("<title>" + str[1] + "</title></head><body>").getBytes(charSet)); } else { bs.write(("</head><body>").getBytes(charSet)); } bs.write(("<center><h3>" + str[0] + "</h3></center><br>").getBytes(charSet)); if (str.length > 1) { bs.write(("<center><h1>" + str[1] + "</h1></center>").getBytes(charSet)); } while ((line = reader.readLine()) != null) { bs.write(line.getBytes(charSet)); } bs.write("</body></html>".getBytes(charSet)); bs.flush(); bs.close(); } catch (IOException e) { result = false; } finally { if (bs != null) { try { bs.close(); } catch (IOException e) { LOGW(Constants.APP_TAG, "Could not closed saved html page", e); } } else if (fs != null) { try { fs.close(); } catch (IOException e) { LOGW(Constants.APP_TAG, "Could not closed saved html page", e); } } } return result; } }