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.data.DiasporaPodList.DiasporaPod;
import com.github.dfa.diaspora_android.web.ProxyHandler; 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.JSONException;
import org.json.JSONObject; 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*. * Created by gsantner (http://gsantner.net/) on 20.03.16. Part of dandelion*.
*/ */
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public class AppSettings extends AppSettingsBase { public class AppSettings extends SharedPreferencesPropertyBackend {
private final SharedPreferences _prefPod; private final SharedPreferences _prefPod;
private DiasporaPod currentPod0Cached; private DiasporaPod currentPod0Cached;

View File

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

View File

@ -8,11 +8,11 @@
* https://www.apache.org/licenses/LICENSE-2.0 * https://www.apache.org/licenses/LICENSE-2.0
* *
#########################################################*/ #########################################################*/
package net.gsantner.opoc.util; package net.gsantner.opoc.preference;
import java.util.List; import java.util.List;
@SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection"}) @SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection", "unused", "SameParameterValue"})
public interface PropertyBackend<TKEY, TTHIS> { public interface PropertyBackend<TKEY, TTHIS> {
String getString(TKEY key, String defaultValue); 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.annotation.SuppressLint;
import android.content.Context; 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 * Default SharedPreference (_prefApp) will be taken if no SP is specified, else the first one
*/ */
@SuppressWarnings({"WeakerAccess", "unused", "SpellCheckingInspection", "SameParameterValue"}) @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 = "%%%";
protected static final String ARRAY_SEPARATOR_SUBSTITUTE = "§§§"; protected static final String ARRAY_SEPARATOR_SUBSTITUTE = "§§§";
public static final String SHARED_PREF_APP = "app"; public static final String SHARED_PREF_APP = "app";
//######################## //
//## Members, Constructors // Members, Constructors
//######################## //
protected final SharedPreferences _prefApp; protected final SharedPreferences _prefApp;
protected final String _prefAppName; protected final String _prefAppName;
protected final Context _context; protected final Context _context;
public AppSettingsBase(final Context context) { public SharedPreferencesPropertyBackend(final Context context) {
this(context, SHARED_PREF_APP); this(context, SHARED_PREF_APP);
} }
public AppSettingsBase(final Context context, final String prefAppName) { public SharedPreferencesPropertyBackend(final Context context, final String prefAppName) {
_context = context.getApplicationContext(); _context = context.getApplicationContext();
_prefAppName = TextUtils.isEmpty(prefAppName) ? _prefAppName = TextUtils.isEmpty(prefAppName) ?
_context.getPackageName() + "_preferences" : prefAppName; _context.getPackageName() + "_preferences" : prefAppName;
_prefApp = _context.getSharedPreferences(_prefAppName, Context.MODE_PRIVATE); _prefApp = _context.getSharedPreferences(_prefAppName, Context.MODE_PRIVATE);
} }
//##################### //
//## Methods // Methods
//##################### //
public Context getContext() { public Context getContext() {
return _context; return _context;
} }
@ -136,9 +136,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return (pref != null && pref.length > 0 ? pref[0] : _prefApp); return (pref != null && pref.length > 0 ? pref[0] : _prefApp);
} }
//################################# //
//## Getter for resources // Getter for resources
//################################# //
public String rstr(@StringRes int stringKeyResourceId) { public String rstr(@StringRes int stringKeyResourceId) {
return _context.getString(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) { public void setString(@StringRes int keyResourceId, String value, final SharedPreferences... pref) {
gp(pref).edit().putString(rstr(keyResourceId), value).apply(); gp(pref).edit().putString(rstr(keyResourceId), value).apply();
} }
@ -235,9 +235,9 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
return getStringListOne(key, gp(pref)); return getStringListOne(key, gp(pref));
} }
//################################# //
//## Getter & Setter for integer // Getter & Setter for integer
//################################# //
public void setInt(@StringRes int keyResourceId, int value, final SharedPreferences... pref) { public void setInt(@StringRes int keyResourceId, int value, final SharedPreferences... pref) {
gp(pref).edit().putInt(rstr(keyResourceId), value).apply(); 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) { public void setLong(@StringRes int keyResourceId, long value, final SharedPreferences... pref) {
gp(pref).edit().putLong(rstr(keyResourceId), value).apply(); 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); 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) { public void setFloat(@StringRes int keyResourceId, float value, final SharedPreferences... pref) {
gp(pref).edit().putFloat(rstr(keyResourceId), value).apply(); 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); 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) { public void setDouble(@StringRes int keyResourceId, double value, final SharedPreferences... pref) {
setLong(rstr(keyResourceId), Double.doubleToRawLongBits(value)); 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))); 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) { public void setBool(@StringRes int keyResourceId, boolean value, final SharedPreferences... pref) {
gp(pref).edit().putBoolean(rstr(keyResourceId), value).apply(); 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); 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) { public int getColor(String key, @ColorRes int defaultColor, final SharedPreferences... pref) {
return gp(pref).getInt(key, rcolor(defaultColor)); return gp(pref).getInt(key, rcolor(defaultColor));
} }
@ -450,49 +450,49 @@ public class AppSettingsBase implements PropertyBackend<String, AppSettingsBase>
} }
@Override @Override
public AppSettingsBase setString(String key, String value) { public SharedPreferencesPropertyBackend setString(String key, String value) {
setString(key, value, _prefApp); setString(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setInt(String key, int value) { public SharedPreferencesPropertyBackend setInt(String key, int value) {
setInt(key, value, _prefApp); setInt(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setLong(String key, long value) { public SharedPreferencesPropertyBackend setLong(String key, long value) {
setLong(key, value, _prefApp); setLong(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setBool(String key, boolean value) { public SharedPreferencesPropertyBackend setBool(String key, boolean value) {
setBool(key, value, _prefApp); setBool(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setFloat(String key, float value) { public SharedPreferencesPropertyBackend setFloat(String key, float value) {
setFloat(key, value, _prefApp); setFloat(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setDouble(String key, double value) { public SharedPreferencesPropertyBackend setDouble(String key, double value) {
setDouble(key, value, _prefApp); setDouble(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setIntList(String key, List<Integer> value) { public SharedPreferencesPropertyBackend setIntList(String key, List<Integer> value) {
setIntListOne(key, value, _prefApp); setIntListOne(key, value, _prefApp);
return this; return this;
} }
@Override @Override
public AppSettingsBase setStringList(String key, List<String> value) { public SharedPreferencesPropertyBackend setStringList(String key, List<String> value) {
setStringListOne(key, value, _prefApp); setStringListOne(key, value, _prefApp);
return this; return this;
} }

View File

@ -14,7 +14,6 @@ import android.annotation.SuppressLint;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
@ -28,6 +27,7 @@ import android.graphics.Color;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable; import android.graphics.drawable.VectorDrawable;
@ -65,8 +65,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 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"}) @SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection"})
public class ContextUtils { public class ContextUtils {
//######################## //
//## Members, Constructors // Members, Constructors
//######################## //
protected Context _context; protected Context _context;
public ContextUtils(Context context) { public ContextUtils(Context context) {
@ -87,57 +85,81 @@ public class ContextUtils {
return _context; return _context;
} }
//########################
//## Resources //
//######################## // Class Methods
static class ResType { //
public static final String DRAWABLE = "drawable"; public enum ResType {
public static final String STRING = "string"; ID, BOOL, INTEGER, COLOR, STRING, ARRAY, DRAWABLE, PLURALS,
public static final String PLURAL = "plural"; ANIM, ATTR, DIMEN, LAYOUT, MENU, RAW, STYLE, XML,
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";
} }
/**
* 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) { public String rstr(@StringRes int strResId) {
return _context.getString(strResId); return _context.getString(strResId);
} }
/**
* Get String by given string ressource identifier (textual)
*/
public String rstr(String strResKey) { 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) { public Drawable rdrawable(@DrawableRes int resId) {
return ContextCompat.getDrawable(_context, resId); return ContextCompat.getDrawable(_context, resId);
} }
/**
* Get color by given color ressource id
*/
public int rcolor(@ColorRes int resId) { public int rcolor(@ColorRes int resId) {
return ContextCompat.getColor(_context, resId); return ContextCompat.getColor(_context, resId);
} }
public int getResId(final String type, final String name) { /**
return _context.getResources().getIdentifier(name, type, _context.getPackageName()); * Checks if all given (textual) ressource ids are available
} *
* @param resType A {@link ResType}
public boolean areResIdsAvailable(final String type, final String... names) { * @param resIdsTextual A (textual) identifier to be awaited at R.restype.resIdsTextual
for (String name : names) { * @return True if all given ids are available
if (getResId(type, name) == 0) { */
public boolean areRessourcesAvailable(final ResType resType, final String... resIdsTextual) {
for (String name : resIdsTextual) {
if (getResId(resType, name) == 0) {
return false; return false;
} }
} }
return true; return true;
} }
//######################## /**
//## Methods * Convert an int color to a hex string. Optionally including alpha value.
//######################## *
* @param intColor The color coded in int
public String colorToHexString(int intColor) { * @param withAlpha Optional; Set first bool parameter to true to also include alpha value
return String.format("#%06X", 0xFFFFFF & intColor); */
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() { 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) { public void openWebpageInExternalBrowser(final String url) {
Uri uri = Uri.parse(url); Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri); 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. * Falls back to applicationId of the app which may differ from manifest.
*/ */
public Object getBuildConfigValue(String fieldName) { public Object getBuildConfigValue(String fieldName) {
String pkg; String pkg = rstr("manifest_package_id");
try { pkg = (pkg != null ? pkg : _context.getPackageName()) + ".BuildConfig";
pkg = rstr("manifest_package_id");
} catch (Resources.NotFoundException ex) {
pkg = _context.getPackageName();
}
pkg += ".BuildConfig";
try { try {
Class<?> c = Class.forName(pkg); Class<?> c = Class.forName(pkg);
return c.getField(fieldName).get(null); 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); Object field = getBuildConfigValue(fieldName);
if (field != null && field instanceof Boolean) { if (field != null && field instanceof Boolean) {
return (Boolean) field; return (Boolean) field;
@ -195,6 +219,9 @@ public class ContextUtils {
return defaultValue; return defaultValue;
} }
/**
* Get a BuildConfig string value
*/
public String bcstr(String fieldName, String defaultValue) { public String bcstr(String fieldName, String defaultValue) {
Object field = getBuildConfigValue(fieldName); Object field = getBuildConfigValue(fieldName);
if (field != null && field instanceof String) { if (field != null && field instanceof String) {
@ -203,27 +230,36 @@ public class ContextUtils {
return defaultValue; return defaultValue;
} }
/**
* Check if this is a gplay build (requires BuildConfig field)
*/
public boolean isGooglePlayBuild() { public boolean isGooglePlayBuild() {
return bcbool("IS_GPLAY_BUILD", true); return bcbool("IS_GPLAY_BUILD", true);
} }
/**
* Check if this is a foss build (requires BuildConfig field)
*/
public boolean isFossBuild() { public boolean isFossBuild() {
return bcbool("IS_FOSS_BUILD", false); 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()) { if (!isGooglePlayBuild()) {
String btcUri = String.format("bitcoin:%s?amount=%s&label=%s&message=%s", String btcUri = String.format("bitcoin:%s?amount=%s&label=%s&message=%s",
rstr(strResBitcoinId), rstr(strResBitcoinAmount), rstr(srBitcoinId), rstr(srBitcoinAmount),
rstr(strResBitcoinMessage), rstr(strResBitcoinMessage)); rstr(srBitcoinMessage), rstr(srBitcoinMessage));
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(btcUri)); intent.setData(Uri.parse(btcUri));
intent.addFlags(FLAG_ACTIVITY_NEW_TASK); intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
try { try {
_context.startActivity(intent); _context.startActivity(intent);
} catch (ActivityNotFoundException e) { } 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) { public boolean isAppInstalled(String packageName) {
PackageManager pm = _context.getApplicationContext().getPackageManager(); PackageManager pm = _context.getApplicationContext().getPackageManager();
try { try {
@ -282,6 +321,9 @@ public class ContextUtils {
} }
} }
/**
* Restart the current app. Supply the class to start on startup
*/
public void restartApp(Class classToStart) { public void restartApp(Class classToStart) {
Intent inte = new Intent(_context, classToStart); Intent inte = new Intent(_context, classToStart);
PendingIntent inteP = PendingIntent.getActivity(_context, 555, inte, PendingIntent.FLAG_CANCEL_CURRENT); PendingIntent inteP = PendingIntent.getActivity(_context, 555, inte, PendingIntent.FLAG_CANCEL_CURRENT);
@ -295,6 +337,10 @@ public class ContextUtils {
Runtime.getRuntime().exit(0); 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) { public String loadMarkdownForTextViewFromRaw(@RawRes int rawMdFile, String prepend) {
try { try {
return new SimpleMarkdownParser() 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) { public void setHtmlToTextView(TextView textView, String html) {
textView.setMovementMethod(LinkMovementMethod.getInstance()); textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(new SpannableString(htmlToSpanned(html))); textView.setText(new SpannableString(htmlToSpanned(html)));
} }
/**
* Estimate this device's screen diagonal size in inches
*/
public double getEstimatedScreenSizeInches() { public double getEstimatedScreenSizeInches() {
DisplayMetrics dm = _context.getResources().getDisplayMetrics(); DisplayMetrics dm = _context.getResources().getDisplayMetrics();
double density = dm.density * 160; double calc = dm.density * 160d;
double x = Math.pow(dm.widthPixels / density, 2); double x = Math.pow(dm.widthPixels / calc, 2);
double y = Math.pow(dm.heightPixels / density, 2); double y = Math.pow(dm.heightPixels / calc, 2);
double screenInches = Math.sqrt(x + y) * 1.16; // 1.16 = est. Nav/Statusbar calc = Math.sqrt(x + y) * 1.16; // 1.16 = est. Nav/Statusbar
screenInches = screenInches < 4.0 ? 4.0 : screenInches; return Math.min(12, Math.max(4, calc));
screenInches = screenInches > 12.0 ? 12.0 : screenInches;
return screenInches;
} }
/**
* Check if the device is currently in portrait orientation
*/
public boolean isInPortraitMode() { public boolean isInPortraitMode() {
return _context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; return _context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
} }
public Locale getLocaleByAndroidCode(String code) { /**
if (!TextUtils.isEmpty(code)) { * Get an {@link Locale} out of a android language code
return code.contains("-r") * The {@code androidLC} may be in any of the forms: de, en, de-rAt
? new Locale(code.substring(0, 2), code.substring(4, 6)) // de-rAt */
: new Locale(code); // de 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; return Resources.getSystem().getConfiguration().locale;
} }
// en/de/de-rAt ; Empty string -> default locale /**
public void setAppLanguage(String androidLocaleString) { * Set the apps language
Locale locale = getLocaleByAndroidCode(androidLocaleString); * {@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(); Configuration config = _context.getResources().getConfiguration();
config.locale = (locale != null && !androidLocaleString.isEmpty()) config.locale = (locale != null && !androidLC.isEmpty())
? locale : Resources.getSystem().getConfiguration().locale; ? locale : Resources.getSystem().getConfiguration().locale;
_context.getResources().updateConfiguration(config, null); _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) { public boolean shouldColorOnTopBeLight(@ColorInt int colorOnBottomInt) {
return 186 > (((0.299 * Color.red(colorOnBottomInt)) return 186 > (((0.299 * Color.red(colorOnBottomInt))
+ ((0.587 * Color.green(colorOnBottomInt)) + ((0.587 * Color.green(colorOnBottomInt))
+ (0.114 * Color.blue(colorOnBottomInt))))); + (0.114 * Color.blue(colorOnBottomInt)))));
} }
/**
* Convert a html string to an android {@link Spanned} object
*/
public Spanned htmlToSpanned(String html) { public Spanned htmlToSpanned(String html) {
Spanned result; Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
@ -364,61 +432,34 @@ public class ContextUtils {
return result; return result;
} }
public boolean setClipboard(String text) { /**
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) { * Convert pixel unit do android dp unit
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;
}
public float convertPxToDp(final float px) { public float convertPxToDp(final float px) {
return px / _context.getResources().getDisplayMetrics().density; return px / _context.getResources().getDisplayMetrics().density;
} }
/**
* Convert android dp unit to pixel unit
*/
public float convertDpToPx(final float dp) { public float convertDpToPx(final float dp) {
return dp * _context.getResources().getDisplayMetrics().density; 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) { public static void setDrawableWithColorToImageView(ImageView imageView, @DrawableRes int drawableResId, @ColorRes int colorResId) {
imageView.setImageResource(drawableResId); imageView.setImageResource(drawableResId);
imageView.setColorFilter(ContextCompat.getColor(imageView.getContext(), colorResId)); imageView.setColorFilter(ContextCompat.getColor(imageView.getContext(), colorResId));
} }
/**
* Get a {@link Bitmap} out of a {@link Drawable}
*/
public Bitmap drawableToBitmap(Drawable drawable) { public Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap = null; 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) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = (DrawableCompat.wrap(drawable)).mutate(); drawable = (DrawableCompat.wrap(drawable)).mutate();
} }
@ -434,6 +475,18 @@ public class ContextUtils {
return bitmap; 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) { public Bitmap loadImageFromFilesystem(File imagePath, int maxDimen) {
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
@ -462,6 +515,10 @@ public class ContextUtils {
return inSampleSize; 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) { public Bitmap scaleBitmap(Bitmap bitmap, int maxDimen) {
int picSize = Math.min(bitmap.getHeight(), bitmap.getWidth()); int picSize = Math.min(bitmap.getHeight(), bitmap.getWidth());
float scale = 1.f * maxDimen / picSize; 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); 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) { public boolean writeImageToFileJpeg(File imageFile, Bitmap image) {
return writeImageToFile(imageFile, image, Bitmap.CompressFormat.JPEG, 95); 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 targetFile The file to be written in
* @param image The image as android {@link Bitmap} * @param image The image as android {@link Bitmap}
@ -517,10 +577,14 @@ public class ContextUtils {
return false; 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(); Resources resources = _context.getResources();
float scale = resources.getDisplayMetrics().density; float scale = resources.getDisplayMetrics().density;
Bitmap bitmap = bitmapToDrawable(resId); Bitmap bitmap = drawableToBitmap(drawableRes);
bitmap = bitmap.copy(bitmap.getConfig(), true); bitmap = bitmap.copy(bitmap.getConfig(), true);
Canvas canvas = new Canvas(bitmap); Canvas canvas = new Canvas(bitmap);
@ -538,24 +602,9 @@ public class ContextUtils {
return bitmap; return bitmap;
} }
public Bitmap bitmapToDrawable(@DrawableRes int drawableId) { /**
Bitmap bitmap = null; * Try to tint all {@link Menu}s {@link MenuItem}s with given color
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;
}
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public void tintMenuItems(Menu menu, boolean recurse, @ColorInt int iconColor) { public void tintMenuItems(Menu menu, boolean recurse, @ColorInt int iconColor) {
for (int i = 0; i < menu.size(); i++) { 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) { 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) { public Drawable tintDrawable(@Nullable Drawable drawable, @ColorInt int color) {
if (drawable != null) { if (drawable != null) {
drawable = DrawableCompat.wrap(drawable); drawable = DrawableCompat.wrap(drawable);
@ -579,6 +634,10 @@ public class ContextUtils {
return drawable; 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) { public void setSubMenuIconsVisiblity(Menu menu, boolean visible) {
if (menu.getClass().getSimpleName().equals("MenuBuilder")) { if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
try { try {