Update gsantner-opoc to latest state

This commit is contained in:
Gregor Santner 2018-03-30 00:14:54 +02:00
parent 43a2ad25bc
commit 586875e32b
No known key found for this signature in database
GPG Key ID: 7E83A7834AECB009
5 changed files with 233 additions and 174 deletions

View File

@ -26,7 +26,7 @@ import com.github.dfa.diaspora_android.data.DiasporaAspect;
import com.github.dfa.diaspora_android.data.DiasporaPodList.DiasporaPod;
import com.github.dfa.diaspora_android.web.ProxyHandler;
import net.gsantner.opoc.util.AppSettingsBase;
import net.gsantner.opoc.preference.SharedPreferencesPropertyBackend;
import org.json.JSONException;
import org.json.JSONObject;
@ -38,7 +38,7 @@ import java.util.List;
* Created by gsantner (http://gsantner.net/) on 20.03.16. Part of dandelion*.
*/
@SuppressWarnings("ConstantConditions")
public class AppSettings extends AppSettingsBase {
public class AppSettings extends SharedPreferencesPropertyBackend {
private final SharedPreferences _prefPod;
private DiasporaPod currentPod0Cached;

View File

@ -33,7 +33,7 @@ import java.io.InputStreamReader;
/**
* Simple Markdown Parser
*/
@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown", "SameParameterValue", "unused", "SpellCheckingInspection", "RepeatedSpace", "SingleCharAlternation"})
@SuppressWarnings({"WeakerAccess", "CaughtExceptionImmediatelyRethrown", "SameParameterValue", "unused", "SpellCheckingInspection", "RepeatedSpace", "SingleCharAlternation", "Convert2Lambda"})
public class SimpleMarkdownParser {
//########################
//## Statics

View File

@ -8,11 +8,11 @@
* https://www.apache.org/licenses/LICENSE-2.0
*
#########################################################*/
package net.gsantner.opoc.util;
package net.gsantner.opoc.preference;
import java.util.List;
@SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection"})
@SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection", "unused", "SameParameterValue"})
public interface PropertyBackend<TKEY, TTHIS> {
String getString(TKEY key, String defaultValue);

View File

@ -31,7 +31,7 @@
}
*/
package net.gsantner.opoc.util;
package net.gsantner.opoc.preference;
import android.annotation.SuppressLint;
import android.content.Context;
@ -48,36 +48,36 @@ import java.util.List;
/**
* Wrapper for settings based on SharedPreferences with keys in resources
* Wrapper for settings based on SharedPreferences, optionally with keys in resources
* Default SharedPreference (_prefApp) will be taken if no SP is specified, else the first one
*/
@SuppressWarnings({"WeakerAccess", "unused", "SpellCheckingInspection", "SameParameterValue"})
public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase> {
public class SharedPreferencesPropertyBackend implements PropertyBackend<String, SharedPreferencesPropertyBackend> {
protected static final String ARRAY_SEPARATOR = "%%%";
protected static final String ARRAY_SEPARATOR_SUBSTITUTE = "§§§";
public static final String SHARED_PREF_APP = "app";
//########################
//## Members, Constructors
//########################
//
// Members, Constructors
//
protected final SharedPreferences _prefApp;
protected final String _prefAppName;
protected final Context _context;
public AppSettingsBase(final Context context) {
public SharedPreferencesPropertyBackend(final Context context) {
this(context, SHARED_PREF_APP);
}
public AppSettingsBase(final Context context, final String prefAppName) {
public SharedPreferencesPropertyBackend(final Context context, final String prefAppName) {
_context = context.getApplicationContext();
_prefAppName = TextUtils.isEmpty(prefAppName) ?
_context.getPackageName() + "_preferences" : prefAppName;
_prefApp = _context.getSharedPreferences(_prefAppName, Context.MODE_PRIVATE);
}
//#####################
//## Methods
//#####################
//
// Methods
//
public Context getContext() {
return _context;
}
@ -136,9 +136,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return (pref != null && pref.length > 0 ? pref[0] : _prefApp);
}
//#################################
//## Getter for resources
//#################################
//
// Getter for resources
//
public String rstr(@StringRes int stringKeyResourceId) {
return _context.getString(stringKeyResourceId);
}
@ -148,9 +148,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
}
//#################################
//## Getter & Setter for String
//#################################
//
// Getter & Setter for String
//
public void setString(@StringRes int keyResourceId, String value, final SharedPreferences... pref) {
gp(pref).edit().putString(rstr(keyResourceId), value).apply();
}
@ -235,9 +235,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return getStringListOne(key, gp(pref));
}
//#################################
//## Getter & Setter for integer
//#################################
//
// Getter & Setter for integer
//
public void setInt(@StringRes int keyResourceId, int value, final SharedPreferences... pref) {
gp(pref).edit().putInt(rstr(keyResourceId), value).apply();
}
@ -319,9 +319,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
}
//#################################
//## Getter & Setter for Long
//#################################
//
// Getter & Setter for Long
//
public void setLong(@StringRes int keyResourceId, long value, final SharedPreferences... pref) {
gp(pref).edit().putLong(rstr(keyResourceId), value).apply();
}
@ -338,9 +338,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return gp(pref).getLong(key, defaultValue);
}
//#################################
//## Getter & Setter for Float
//#################################
//
// Getter & Setter for Float
//
public void setFloat(@StringRes int keyResourceId, float value, final SharedPreferences... pref) {
gp(pref).edit().putFloat(rstr(keyResourceId), value).apply();
}
@ -357,9 +357,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return gp(pref).getFloat(key, defaultValue);
}
//#################################
//## Getter & Setter for Double
//#################################
//
// Getter & Setter for Double
//
public void setDouble(@StringRes int keyResourceId, double value, final SharedPreferences... pref) {
setLong(rstr(keyResourceId), Double.doubleToRawLongBits(value));
}
@ -376,9 +376,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return Double.longBitsToDouble(getLong(key, Double.doubleToRawLongBits(defaultValue), gp(pref)));
}
//#################################
//## Getter & Setter for boolean
//#################################
//
// Getter & Setter for boolean
//
public void setBool(@StringRes int keyResourceId, boolean value, final SharedPreferences... pref) {
gp(pref).edit().putBoolean(rstr(keyResourceId), value).apply();
}
@ -395,9 +395,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return gp(pref).getBoolean(key, defaultValue);
}
//#################################
//## Getter & Setter for Color
//#################################
//
// Getter & Setter for Color
//
public int getColor(String key, @ColorRes int defaultColor, final SharedPreferences... pref) {
return gp(pref).getInt(key, rcolor(defaultColor));
}
@ -450,49 +450,49 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
}
@Override
public AppSettingsBase setString(String key, String value) {
public SharedPreferencesPropertyBackend setString(String key, String value) {
setString(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setInt(String key, int value) {
public SharedPreferencesPropertyBackend setInt(String key, int value) {
setInt(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setLong(String key, long value) {
public SharedPreferencesPropertyBackend setLong(String key, long value) {
setLong(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setBool(String key, boolean value) {
public SharedPreferencesPropertyBackend setBool(String key, boolean value) {
setBool(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setFloat(String key, float value) {
public SharedPreferencesPropertyBackend setFloat(String key, float value) {
setFloat(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setDouble(String key, double value) {
public SharedPreferencesPropertyBackend setDouble(String key, double value) {
setDouble(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setIntList(String key, List<Integer> value) {
public SharedPreferencesPropertyBackend setIntList(String key, List<Integer> value) {
setIntListOne(key, value, _prefApp);
return this;
}
@Override
public AppSettingsBase setStringList(String key, List<String> value) {
public SharedPreferencesPropertyBackend setStringList(String key, List<String> value) {
setStringListOne(key, value, _prefApp);
return this;
}

View File

@ -14,7 +14,6 @@ import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@ -28,6 +27,7 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
@ -65,8 +65,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@ -74,9 +72,9 @@ import static android.graphics.Bitmap.CompressFormat;
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection"})
public class ContextUtils {
//########################
//## Members, Constructors
//########################
//
// Members, Constructors
//
protected Context _context;
public ContextUtils(Context context) {
@ -87,57 +85,81 @@ public class ContextUtils {
return _context;
}
//########################
//## Resources
//########################
static class ResType {
public static final String DRAWABLE = "drawable";
public static final String STRING = "string";
public static final String PLURAL = "plural";
public static final String COLOR = "color";
public static final String STYLE = "style";
public static final String ARRAY = "array";
public static final String DIMEN = "dimen";
public static final String MENU = "menu";
public static final String BOOL = "bool";
public static final String RAW = "raw";
//
// Class Methods
//
public enum ResType {
ID, BOOL, INTEGER, COLOR, STRING, ARRAY, DRAWABLE, PLURALS,
ANIM, ATTR, DIMEN, LAYOUT, MENU, RAW, STYLE, XML,
}
/**
* Find out the nuermical ressource id by given {@link ResType}
*
* @return A valid id if the id could be found, else 0
*/
public int getResId(ResType resType, final String name) {
return _context.getResources().getIdentifier(name, resType.name().toLowerCase(), _context.getPackageName());
}
/**
* Get String by given string ressource id (nuermic)
*/
public String rstr(@StringRes int strResId) {
return _context.getString(strResId);
}
/**
* Get String by given string ressource identifier (textual)
*/
public String rstr(String strResKey) {
return rstr(getResId(ResType.STRING, strResKey));
try {
return rstr(getResId(ResType.STRING, strResKey));
} catch (Resources.NotFoundException e) {
return null;
}
}
/**
* Get drawable from given ressource identifier
*/
public Drawable rdrawable(@DrawableRes int resId) {
return ContextCompat.getDrawable(_context, resId);
}
/**
* Get color by given color ressource id
*/
public int rcolor(@ColorRes int resId) {
return ContextCompat.getColor(_context, resId);
}
public int getResId(final String type, final String name) {
return _context.getResources().getIdentifier(name, type, _context.getPackageName());
}
public boolean areResIdsAvailable(final String type, final String... names) {
for (String name : names) {
if (getResId(type, name) == 0) {
/**
* Checks if all given (textual) ressource ids are available
*
* @param resType A {@link ResType}
* @param resIdsTextual A (textual) identifier to be awaited at R.restype.resIdsTextual
* @return True if all given ids are available
*/
public boolean areRessourcesAvailable(final ResType resType, final String... resIdsTextual) {
for (String name : resIdsTextual) {
if (getResId(resType, name) == 0) {
return false;
}
}
return true;
}
//########################
//## Methods
//########################
public String colorToHexString(int intColor) {
return String.format("#%06X", 0xFFFFFF & intColor);
/**
* Convert an int color to a hex string. Optionally including alpha value.
*
* @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) {
boolean a = withAlpha != null && withAlpha.length >= 1 && withAlpha[0];
return String.format(a ? "#%08X" : "#%06X", (a ? 0xFFFFFFFF : 0xFFFFFF) & intColor);
}
public String getAppVersionName() {
@ -151,6 +173,10 @@ public class ContextUtils {
}
}
/**
* Send a {@link Intent#ACTION_VIEW} Intent with given paramter
* If the parameter is an string a browser will get triggered
*/
public void openWebpageInExternalBrowser(final String url) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
@ -171,13 +197,8 @@ public class ContextUtils {
* Falls back to applicationId of the app which may differ from manifest.
*/
public Object getBuildConfigValue(String fieldName) {
String pkg;
try {
pkg = rstr("manifest_package_id");
} catch (Resources.NotFoundException ex) {
pkg = _context.getPackageName();
}
pkg += ".BuildConfig";
String pkg = rstr("manifest_package_id");
pkg = (pkg != null ? pkg : _context.getPackageName()) + ".BuildConfig";
try {
Class<?> c = Class.forName(pkg);
return c.getField(fieldName).get(null);
@ -187,7 +208,10 @@ public class ContextUtils {
}
}
public boolean bcbool(String fieldName, boolean defaultValue) {
/**
* Get a BuildConfig bool value
*/
public Boolean bcbool(String fieldName, Boolean defaultValue) {
Object field = getBuildConfigValue(fieldName);
if (field != null && field instanceof Boolean) {
return (Boolean) field;
@ -195,6 +219,9 @@ public class ContextUtils {
return defaultValue;
}
/**
* Get a BuildConfig string value
*/
public String bcstr(String fieldName, String defaultValue) {
Object field = getBuildConfigValue(fieldName);
if (field != null && field instanceof String) {
@ -203,27 +230,36 @@ public class ContextUtils {
return defaultValue;
}
/**
* Check if this is a gplay build (requires BuildConfig field)
*/
public boolean isGooglePlayBuild() {
return bcbool("IS_GPLAY_BUILD", true);
}
/**
* Check if this is a foss build (requires BuildConfig field)
*/
public boolean isFossBuild() {
return bcbool("IS_FOSS_BUILD", false);
}
// Requires donate__bitcoin_* resources (see below) to be available as string resource
public void showDonateBitcoinRequest(@StringRes final int strResBitcoinId, @StringRes final int strResBitcoinAmount, @StringRes final int strResBitcoinMessage, @StringRes final int strResAlternativeDonateUrl) {
/**
* Request a bitcoin donation with given details.
* All parameters are awaited as string resource ids
*/
public void showDonateBitcoinRequest(@StringRes final int srBitcoinId, @StringRes final int srBitcoinAmount, @StringRes final int srBitcoinMessage, @StringRes final int srAlternativeDonateUrl) {
if (!isGooglePlayBuild()) {
String btcUri = String.format("bitcoin:%s?amount=%s&label=%s&message=%s",
rstr(strResBitcoinId), rstr(strResBitcoinAmount),
rstr(strResBitcoinMessage), rstr(strResBitcoinMessage));
rstr(srBitcoinId), rstr(srBitcoinAmount),
rstr(srBitcoinMessage), rstr(srBitcoinMessage));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(btcUri));
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
try {
_context.startActivity(intent);
} catch (ActivityNotFoundException e) {
openWebpageInExternalBrowser(rstr(strResAlternativeDonateUrl));
openWebpageInExternalBrowser(rstr(srAlternativeDonateUrl));
}
}
}
@ -272,6 +308,9 @@ public class ContextUtils {
}
}
/**
* Check if app with given {@code packageName} is installed
*/
public boolean isAppInstalled(String packageName) {
PackageManager pm = _context.getApplicationContext().getPackageManager();
try {
@ -282,6 +321,9 @@ public class ContextUtils {
}
}
/**
* Restart the current app. Supply the class to start on startup
*/
public void restartApp(Class classToStart) {
Intent inte = new Intent(_context, classToStart);
PendingIntent inteP = PendingIntent.getActivity(_context, 555, inte, PendingIntent.FLAG_CANCEL_CURRENT);
@ -295,6 +337,10 @@ public class ContextUtils {
Runtime.getRuntime().exit(0);
}
/**
* Load a markdown file from a {@link RawRes}, prepend each line with {@code prepend} text
* and convert markdown to html using {@link SimpleMarkdownParser}
*/
public String loadMarkdownForTextViewFromRaw(@RawRes int rawMdFile, String prepend) {
try {
return new SimpleMarkdownParser()
@ -308,52 +354,74 @@ public class ContextUtils {
}
}
/**
* Load html into a {@link Spanned} object and set the
* {@link TextView}'s text using {@link TextView#setText(CharSequence)}
*/
public void setHtmlToTextView(TextView textView, String html) {
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(new SpannableString(htmlToSpanned(html)));
}
/**
* Estimate this device's screen diagonal size in inches
*/
public double getEstimatedScreenSizeInches() {
DisplayMetrics dm = _context.getResources().getDisplayMetrics();
double density = dm.density * 160;
double x = Math.pow(dm.widthPixels / density, 2);
double y = Math.pow(dm.heightPixels / density, 2);
double screenInches = Math.sqrt(x + y) * 1.16; // 1.16 = est. Nav/Statusbar
screenInches = screenInches < 4.0 ? 4.0 : screenInches;
screenInches = screenInches > 12.0 ? 12.0 : screenInches;
return screenInches;
double calc = dm.density * 160d;
double x = Math.pow(dm.widthPixels / calc, 2);
double y = Math.pow(dm.heightPixels / calc, 2);
calc = Math.sqrt(x + y) * 1.16; // 1.16 = est. Nav/Statusbar
return Math.min(12, Math.max(4, calc));
}
/**
* Check if the device is currently in portrait orientation
*/
public boolean isInPortraitMode() {
return _context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
}
public Locale getLocaleByAndroidCode(String code) {
if (!TextUtils.isEmpty(code)) {
return code.contains("-r")
? new Locale(code.substring(0, 2), code.substring(4, 6)) // de-rAt
: new Locale(code); // de
/**
* Get an {@link Locale} out of a android language code
* The {@code androidLC} may be in any of the forms: de, en, de-rAt
*/
public Locale getLocaleByAndroidCode(String androidLC) {
if (!TextUtils.isEmpty(androidLC)) {
return androidLC.contains("-r")
? new Locale(androidLC.substring(0, 2), androidLC.substring(4, 6)) // de-rAt
: new Locale(androidLC); // de
}
return Resources.getSystem().getConfiguration().locale;
}
// en/de/de-rAt ; Empty string -> default locale
public void setAppLanguage(String androidLocaleString) {
Locale locale = getLocaleByAndroidCode(androidLocaleString);
/**
* Set the apps language
* {@code androidLC} may be in any of the forms: en, de, de-rAt
* If given an empty string, the default (system) locale gets loaded
*/
public void setAppLanguage(String androidLC) {
Locale locale = getLocaleByAndroidCode(androidLC);
Configuration config = _context.getResources().getConfiguration();
config.locale = (locale != null && !androidLocaleString.isEmpty())
config.locale = (locale != null && !androidLC.isEmpty())
? locale : Resources.getSystem().getConfiguration().locale;
_context.getResources().updateConfiguration(config, null);
}
// Find out if color above the given color should be light or dark. true if light
/**
* Try to guess if the color on top of the given {@code colorOnBottomInt}
* should be light or dark. Returns true if top color should be light
*/
public boolean shouldColorOnTopBeLight(@ColorInt int colorOnBottomInt) {
return 186 > (((0.299 * Color.red(colorOnBottomInt))
+ ((0.587 * Color.green(colorOnBottomInt))
+ (0.114 * Color.blue(colorOnBottomInt)))));
}
/**
* Convert a html string to an android {@link Spanned} object
*/
public Spanned htmlToSpanned(String html) {
Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
@ -364,61 +432,34 @@ public class ContextUtils {
return result;
}
public boolean setClipboard(String text) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
if (cm != null) {
cm.setText(text);
return true;
}
} else {
android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
if (cm != null) {
ClipData clip = ClipData.newPlainText(_context.getPackageName(), text);
cm.setPrimaryClip(clip);
return true;
}
}
return false;
}
public List<String> getClipboard() {
List<String> clipper = new ArrayList<>();
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
if (cm != null) {
clipper.add(cm.getText().toString());
}
} else {
android.content.ClipboardManager cm = ((android.content.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
if (cm != null && cm.hasPrimaryClip()) {
ClipData data = cm.getPrimaryClip();
for (int i = 0; i < data.getItemCount() && i < data.getItemCount(); i++) {
clipper.add(data.getItemAt(i).getText().toString());
}
}
}
return clipper;
}
/**
* Convert pixel unit do android dp unit
*/
public float convertPxToDp(final float px) {
return px / _context.getResources().getDisplayMetrics().density;
}
/**
* Convert android dp unit to pixel unit
*/
public float convertDpToPx(final float dp) {
return dp * _context.getResources().getDisplayMetrics().density;
}
/**
* Load an image into a {@link ImageView} and apply a color filter
*/
public static void setDrawableWithColorToImageView(ImageView imageView, @DrawableRes int drawableResId, @ColorRes int colorResId) {
imageView.setImageResource(drawableResId);
imageView.setColorFilter(ContextCompat.getColor(imageView.getContext(), colorResId));
}
/**
* Get a {@link Bitmap} out of a {@link Drawable}
*/
public Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && (drawable instanceof VectorDrawable || drawable instanceof VectorDrawableCompat)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && (drawable instanceof VectorDrawable || drawable instanceof VectorDrawableCompat || drawable instanceof AdaptiveIconDrawable)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = (DrawableCompat.wrap(drawable)).mutate();
}
@ -434,6 +475,18 @@ public class ContextUtils {
return bitmap;
}
/**
* Get a {@link Bitmap} out of a {@link DrawableRes}
*/
public Bitmap drawableToBitmap(@DrawableRes int drawableId) {
return drawableToBitmap(ContextCompat.getDrawable(_context, drawableId));
}
/**
* Get a {@link Bitmap} from a given {@code imagePath} on the filesystem
* Specifying a {@code maxDimen} is also possible and a value below 2000
* is recommended, otherwise a {@link OutOfMemoryError} may occur
*/
public Bitmap loadImageFromFilesystem(File imagePath, int maxDimen) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
@ -462,6 +515,10 @@ public class ContextUtils {
return inSampleSize;
}
/**
* Scale the bitmap so both dimensions are lower or equal to {@code maxDimen}
* This keeps the aspect ratio
*/
public Bitmap scaleBitmap(Bitmap bitmap, int maxDimen) {
int picSize = Math.min(bitmap.getHeight(), bitmap.getWidth());
float scale = 1.f * maxDimen / picSize;
@ -470,12 +527,15 @@ public class ContextUtils {
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
/**
* Write the given {@link Bitmap} to {@code imageFile}, in {@link CompressFormat#JPEG} format
*/
public boolean writeImageToFileJpeg(File imageFile, Bitmap image) {
return writeImageToFile(imageFile, image, Bitmap.CompressFormat.JPEG, 95);
}
/**
* Write bitmap to the filesystem
* Write the given {@link Bitmap} to filesystem
*
* @param targetFile The file to be written in
* @param image The image as android {@link Bitmap}
@ -517,10 +577,14 @@ public class ContextUtils {
return false;
}
public Bitmap drawTextOnDrawable(@DrawableRes int resId, String text, int textSize) {
/**
* Draw text in the center of the given {@link DrawableRes}
* This may be useful for e.g. badge counts
*/
public Bitmap drawTextOnDrawable(@DrawableRes int drawableRes, String text, int textSize) {
Resources resources = _context.getResources();
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap = bitmapToDrawable(resId);
Bitmap bitmap = drawableToBitmap(drawableRes);
bitmap = bitmap.copy(bitmap.getConfig(), true);
Canvas canvas = new Canvas(bitmap);
@ -538,24 +602,9 @@ public class ContextUtils {
return bitmap;
}
public Bitmap bitmapToDrawable(@DrawableRes int drawableId) {
Bitmap bitmap = null;
Drawable drawable = ContextCompat.getDrawable(_context, drawableId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && (drawable instanceof VectorDrawable || drawable instanceof VectorDrawableCompat)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = (DrawableCompat.wrap(drawable)).mutate();
}
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
} else if (drawable instanceof BitmapDrawable) {
bitmap = ((BitmapDrawable) drawable).getBitmap();
}
return bitmap;
}
/**
* Try to tint all {@link Menu}s {@link MenuItem}s with given color
*/
@SuppressWarnings("ConstantConditions")
public void tintMenuItems(Menu menu, boolean recurse, @ColorInt int iconColor) {
for (int i = 0; i < menu.size(); i++) {
@ -567,10 +616,16 @@ public class ContextUtils {
}
}
/**
* Loads {@link Drawable} by given {@link DrawableRes} and applies a color
*/
public Drawable tintDrawable(@DrawableRes int drawableRes, @ColorInt int color) {
return tintDrawable(_context.getResources().getDrawable(drawableRes), color);
return tintDrawable(rdrawable(drawableRes), color);
}
/**
* Tint a {@link Drawable} with given {@code color}
*/
public Drawable tintDrawable(@Nullable Drawable drawable, @ColorInt int color) {
if (drawable != null) {
drawable = DrawableCompat.wrap(drawable);
@ -579,6 +634,10 @@ public class ContextUtils {
return drawable;
}
/**
* Try to make icons in Toolbar/ActionBars SubMenus visible
* 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 (menu.getClass().getSimpleName().equals("MenuBuilder")) {
try {