diff --git a/CHANGELOG.md b/CHANGELOG.md index bbd35c31d..071d28fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ -![](https://test.gsantner.net/matomo/piwik.php?action_name=inapp_changelog&idsite=2&rec=1&urlref=https%3A%2F%2Fgithub.com%2Fgsantner%2Fmemetastic%2FCHANGELOG.md&_cvar=%7B%221%22%3A%5B%22source%22%2C%22changelog%22%5D%2C%222%22%3A%5B%22project%22%2C%22memetastic%22%5D%2C%223%22%3A%5B%22packageid%22%2C%22io.github.gsantner.memetastic%22%5D%2C%224%22%3A%5B%22referrer%22%2C%22https%3A%2F%2Fgithub.com%2Fgsantner%2Fmemetastic%2FCHANGELOG.md%22%5D%7D) - +### v1.7 (In progress) +- Add more languages for translation +- Make favourite icon in popup smaller ### v1.6 [Blog post](https://gsantner.net/blog/2019/08/15/memetastic-v1.6-offline-meme-templates-funny-sites.html?source=inapp_changelog&project=memetastic) - Simplify overall app usage diff --git a/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java b/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java index 343f2a4ca..6950e522e 100644 --- a/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java +++ b/app/src/main/java/net/gsantner/opoc/preference/SharedPreferencesPropertyBackend.java @@ -547,7 +547,7 @@ public static boolean isCurrentHourOfDayBetween(int begin, int end) { */ public Date getDateOfDaysAgo(int days) { Calendar cal = new GregorianCalendar(); - cal.add(Calendar.DAY_OF_MONTH, -days); + cal.add(Calendar.DATE, -days); return cal.getTime(); } diff --git a/app/src/main/java/net/gsantner/opoc/util/Callback.java b/app/src/main/java/net/gsantner/opoc/util/Callback.java index c07f883c0..7a3186e80 100644 --- a/app/src/main/java/net/gsantner/opoc/util/Callback.java +++ b/app/src/main/java/net/gsantner/opoc/util/Callback.java @@ -12,6 +12,11 @@ @SuppressWarnings("unused") public class Callback { + + public interface a0 { + void callback(); + } + public interface a1 { void callback(A arg1); } diff --git a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java index f4b730b44..55f78102f 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java @@ -41,6 +41,8 @@ import android.os.Build; import android.os.Environment; import android.os.SystemClock; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; @@ -51,7 +53,9 @@ import android.support.v4.app.ActivityManagerCompat; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v4.text.TextUtilsCompat; import android.support.v4.util.Pair; +import android.support.v4.view.ViewCompat; import android.text.Html; import android.text.InputFilter; import android.text.SpannableString; @@ -82,6 +86,7 @@ import java.util.List; import java.util.Locale; +import static android.content.Context.VIBRATOR_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.graphics.Bitmap.CompressFormat; @@ -150,6 +155,10 @@ public Drawable rdrawable(@DrawableRes int resId) { * Get color by given color ressource id */ public int rcolor(@ColorRes int resId) { + if (resId == 0) { + Log.e(getClass().getName(), "ContextUtils::rcolor: resId is 0!"); + return Color.BLACK; + } return ContextCompat.getColor(_context, resId); } @@ -175,12 +184,12 @@ public boolean areRessourcesAvailable(final ResType resType, final String... res * @param intColor The color coded in int * @param withAlpha Optional; Set first bool parameter to true to also include alpha value */ - public String colorToHexString(int intColor, boolean... withAlpha) { + public static String colorToHexString(int intColor, boolean... withAlpha) { boolean a = withAlpha != null && withAlpha.length >= 1 && withAlpha[0]; return String.format(a ? "#%08X" : "#%06X", (a ? 0xFFFFFFFF : 0xFFFFFF) & intColor); } - public String getAndroidVersion() { + public static String getAndroidVersion() { return Build.VERSION.RELEASE + " (" + Build.VERSION.SDK_INT + ")"; } @@ -286,7 +295,7 @@ public Object getBuildConfigValue(String fieldName) { */ public Boolean bcbool(String fieldName, Boolean defaultValue) { Object field = getBuildConfigValue(fieldName); - if (field != null && field instanceof Boolean) { + if (field instanceof Boolean) { return (Boolean) field; } return defaultValue; @@ -297,7 +306,7 @@ public Boolean bcbool(String fieldName, Boolean defaultValue) { */ public String bcstr(String fieldName, String defaultValue) { Object field = getBuildConfigValue(fieldName); - if (field != null && field instanceof String) { + if (field instanceof String) { return (String) field; } return defaultValue; @@ -308,7 +317,7 @@ public String bcstr(String fieldName, String defaultValue) { */ public Integer bcint(String fieldName, int defaultValue) { Object field = getBuildConfigValue(fieldName); - if (field != null && field instanceof Integer) { + if (field instanceof Integer) { return (Integer) field; } return defaultValue; @@ -490,10 +499,16 @@ public Locale getLocaleByAndroidCode(String androidLC) { */ public void setAppLanguage(String androidLC) { Locale locale = getLocaleByAndroidCode(androidLC); + locale = (locale != null && !androidLC.isEmpty()) ? locale : Resources.getSystem().getConfiguration().locale; + setLocale(locale); + } + + public ContextUtils setLocale(Locale locale) { Configuration config = _context.getResources().getConfiguration(); - config.locale = (locale != null && !androidLC.isEmpty()) - ? locale : Resources.getSystem().getConfiguration().locale; + config.locale = (locale != null ? locale : Resources.getSystem().getConfiguration().locale); _context.getResources().updateConfiguration(config, null); + Locale.setDefault(locale); + return this; } /** @@ -827,6 +842,9 @@ public Drawable tintDrawable(@Nullable Drawable drawable, @ColorInt int color) { * This may not work on some devices and it maybe won't work on future android updates */ public void setSubMenuIconsVisiblity(Menu menu, boolean visible) { + if (TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL) { + return; + } if (menu.getClass().getSimpleName().equals("MenuBuilder")) { try { @SuppressLint("PrivateApi") Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE); @@ -957,6 +975,22 @@ public boolean isDeviceGoodHardware() { return true; } } + + // Vibrate device one time by given amount of time, defaulting to 50ms + // Requires in AndroidManifest to work + @SuppressWarnings("UnnecessaryReturnStatement") + @SuppressLint("MissingPermission") + public void vibrate(int... ms) { + int ms_v = ms != null && ms.length > 0 ? ms[0] : 50; + Vibrator vibrator = ((Vibrator) _context.getSystemService(VIBRATOR_SERVICE)); + if (vibrator == null) { + return; + } else if (Build.VERSION.SDK_INT >= 26) { + vibrator.vibrate(VibrationEffect.createOneShot(ms_v, VibrationEffect.DEFAULT_AMPLITUDE)); + } else { + vibrator.vibrate(ms_v); + } + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java index 14c573447..c8a2b6d3f 100644 --- a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java @@ -29,6 +29,7 @@ import java.io.OutputStream; import java.net.URLConnection; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -240,6 +241,30 @@ public static boolean copyFile(final File src, final File dst) { } } + public static boolean copyFile(final File src, final FileOutputStream os) { + InputStream is = null; + try { + try { + is = new FileInputStream(src); + byte[] buf = new byte[BUFFER_SIZE]; + int len; + while ((len = is.read(buf)) > 0) { + os.write(buf, 0, len); + } + return true; + } finally { + if (is != null) { + is.close(); + } + if (os != null) { + os.close(); + } + } + } catch (IOException ex) { + return false; + } + } + // Returns -1 if the file did not contain any of the needles, otherwise, // the index of which needle was found in the contents of the file. // @@ -452,7 +477,15 @@ public static String getReadableFileSize(long size, boolean abbreviation) { } String[] units = abbreviation ? new String[]{"B", "kB", "MB", "GB", "TB"} : new String[]{"Bytes", "Kilobytes", "Megabytes", "Gigabytes", "Terabytes"}; int unit = (int) (Math.log10(size) / Math.log10(1024)); - return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, unit)) - + " " + units[unit]; + return new DecimalFormat("#,##0.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH)).format(size / Math.pow(1024, unit)) + " " + units[unit]; + } + + public static int[] getTimeDiffHMS(long now, long past) { + int[] ret = new int[3]; + long diff = Math.abs(now - past); + ret[0] = (int) (diff / (1000 * 60 * 60)); // hours + ret[1] = (int) (diff / (1000 * 60)) % 60; // min + ret[2] = (int) (diff / 1000) % 60; // sec + return ret; } } diff --git a/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java b/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java index cc6ae7bb7..c383d3b12 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java +++ b/app/src/main/java/net/gsantner/opoc/util/ShareUtil.java @@ -360,18 +360,23 @@ public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format, int quali */ @RequiresApi(api = Build.VERSION_CODES.KITKAT) @SuppressWarnings("deprecation") - public PrintJob print(WebView webview, String jobName) { + public PrintJob print(final WebView webview, final String jobName, final boolean... landscape) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - PrintDocumentAdapter printAdapter; - PrintManager printManager = (PrintManager) _context.getSystemService(Context.PRINT_SERVICE); + final PrintDocumentAdapter printAdapter; + final PrintManager printManager = (PrintManager) _context.getSystemService(Context.PRINT_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { printAdapter = webview.createPrintDocumentAdapter(jobName); } else { printAdapter = webview.createPrintDocumentAdapter(); } + final PrintAttributes.Builder attrib = new PrintAttributes.Builder(); + if (landscape != null && landscape.length > 0 && landscape[0]) { + attrib.setMediaSize(new PrintAttributes.MediaSize("ISO_A4", "android", 11690, 8270)); + attrib.setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0)); + } if (printManager != null) { try { - return printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build()); + return printManager.print(jobName, printAdapter, attrib.build()); } catch (Exception ignored) { } } @@ -572,6 +577,14 @@ public File extractFileFromIntent(Intent receivingIntent) { } } + // media/ prefix for External storage + if (fileStr.startsWith((tmps = "media/"))) { + File f = new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileStr.substring(tmps.length()))); + if (f.exists()) { + return f; + } + } + // Next/OwnCloud Fileprovider for (String fp : new String[]{"org.nextcloud.files", "org.nextcloud.beta.files", "org.owncloud.files"}) { if (fileProvider.equals(fp) && fileStr.startsWith(tmps = "external_files/")) { @@ -587,6 +600,16 @@ public File extractFileFromIntent(Intent receivingIntent) { return new File(Uri.decode(Environment.getExternalStorageDirectory().getAbsolutePath() + fileStr.substring(tmps.length()))); } + if (fileStr.startsWith(tmps = "external_files/")) { + for (String prefix : new String[]{Environment.getExternalStorageDirectory().getAbsolutePath(), "/storage", ""}) { + File f = new File(Uri.decode(prefix + "/" + fileStr.substring(tmps.length()))); + if (f.exists()) { + return f; + } + } + + } + // URI Encoded paths with full path after content://package/ if (fileStr.startsWith("/") || fileStr.startsWith("%2F")) { tmpf = new File(Uri.decode(fileStr)); @@ -624,6 +647,11 @@ public void requestGalleryPicture() { } } + public String extractFileFromIntentStr(Intent receivingIntent) { + File f = extractFileFromIntent(receivingIntent); + return f != null ? f.getAbsolutePath() : null; + } + /** * Request a picture from camera-like apps * Result ({@link String}) will be available from {@link Activity#onActivityResult(int, int, Intent)}. @@ -647,7 +675,7 @@ public String requestCameraPicture(File target) { if (target != null && !target.isDirectory()) { photoFile = target; } else { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", Locale.getDefault()); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss", Locale.ENGLISH); File storageDir = target != null ? target : new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera"); String imageFileName = ((new ContextUtils(_context).rstr("app_name")).replaceAll("[^a-zA-Z0-9\\.\\-]", "_") + "_").replace("__", "_") + sdf.format(new Date()); photoFile = new File(storageDir, imageFileName + ".jpg"); @@ -717,6 +745,10 @@ public Object extractResultFromActivityResult(int requestCode, int resultCode, I cursor.close(); } + // Try to grab via file extraction method + data.setAction(Intent.ACTION_VIEW); + picturePath = picturePath != null ? picturePath : extractFileFromIntentStr(data); + // Retrieve image from file descriptor / Cloud, e.g.: Google Drive, Picasa if (picturePath == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { @@ -999,7 +1031,8 @@ private Activity greedyGetActivity(Activity... activity) { public boolean canWriteFile(File file, boolean isDir) { if (file == null) { return false; - } else if (file.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { + } else if (file.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) + || file.getAbsolutePath().startsWith(_context.getFilesDir().getAbsolutePath())) { boolean s1 = isDir && file.getParentFile().canWrite(); return !isDir && file.getParentFile() != null ? file.getParentFile().canWrite() : file.canWrite(); } else { @@ -1087,11 +1120,12 @@ public void showMountSdDialog(@StringRes int title, @StringRes int description, dialogi.show(); } + @SuppressWarnings({"ResultOfMethodCallIgnored", "StatementWithEmptyBody"}) public void writeFile(File file, boolean isDirectory, Callback.a2 writeFileCallback) { try { FileOutputStream fileOutputStream = null; ParcelFileDescriptor pfd = null; - if (file.canWrite()) { + if (file.canWrite() || (!file.exists() && file.getParentFile().canWrite())) { if (isDirectory) { file.mkdirs(); } else { @@ -1112,7 +1146,10 @@ public void writeFile(File file, boolean isDirectory, Callback.a2