mirror of
https://github.com/gsantner/dandelion
synced 2024-12-22 02:48:01 +01:00
Update opoc
Update shared helper utilities of my projects to latest state
This commit is contained in:
parent
1948c28cff
commit
7361d4bc3f
9 changed files with 286 additions and 112 deletions
|
@ -16,6 +16,7 @@ import android.support.annotation.LayoutRes;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -143,4 +144,17 @@ public abstract class GsFragmentBase extends Fragment {
|
|||
public Menu getFragmentMenu() {
|
||||
return _fragmentMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the toolbar from activity
|
||||
* Requires id to be set to @+id/toolbar
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
protected Toolbar getToolbar() {
|
||||
try {
|
||||
return (Toolbar) getActivity().findViewById(new ContextUtils(getActivity()).getResId(ContextUtils.ResType.ID, "toolbar"));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -547,7 +547,7 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend<String,
|
|||
*/
|
||||
public Date getDateOfDaysAgo(int days) {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.add(Calendar.DAY_OF_MONTH, -days);
|
||||
cal.add(Calendar.DATE, -days);
|
||||
return cal.getTime();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,11 @@
|
|||
package net.gsantner.opoc.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
@ -23,6 +26,7 @@ import android.support.v7.widget.AppCompatEditText;
|
|||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -56,9 +60,14 @@ public class SearchOrCustomTextDialog {
|
|||
public Callback.a1<String> callback;
|
||||
public List<? extends CharSequence> data = new ArrayList<>();
|
||||
public List<? extends CharSequence> highlightData = new ArrayList<>();
|
||||
public List<Integer> iconsForData = new ArrayList<>();
|
||||
public String messageText = "";
|
||||
public boolean isSearchEnabled = true;
|
||||
public boolean isDarkDialog = false;
|
||||
public int dialogWidthDp = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
public int dialogHeightDp = WindowManager.LayoutParams.WRAP_CONTENT;
|
||||
public int gravity = Gravity.NO_GRAVITY;
|
||||
public int searchInputType = 0;
|
||||
|
||||
@ColorInt
|
||||
public int textColor = 0xFF000000;
|
||||
|
@ -89,6 +98,17 @@ public class SearchOrCustomTextDialog {
|
|||
TextView textView = (TextView) super.getView(pos, convertView, parent);
|
||||
String text = textView.getText().toString();
|
||||
|
||||
int posInOriginalList = dopt.data.indexOf(text);
|
||||
if (posInOriginalList >= 0 && dopt.iconsForData != null && posInOriginalList < dopt.iconsForData.size() && dopt.iconsForData.get(posInOriginalList) != 0) {
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(dopt.iconsForData.get(posInOriginalList), 0, 0, 0);
|
||||
textView.setCompoundDrawablePadding(32);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
textView.setCompoundDrawableTintList(ColorStateList.valueOf(dopt.isDarkDialog ? Color.WHITE : Color.BLACK));
|
||||
}
|
||||
} else {
|
||||
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
boolean hl = dopt.highlightData.contains(text);
|
||||
textView.setTextColor(hl ? dopt.highlightColor : dopt.textColor);
|
||||
textView.setTypeface(null, hl ? Typeface.BOLD : Typeface.NORMAL);
|
||||
|
@ -132,6 +152,7 @@ public class SearchOrCustomTextDialog {
|
|||
searchEditText.setTextColor(dopt.textColor);
|
||||
searchEditText.setHintTextColor((dopt.textColor & 0x00FFFFFF) | 0x99000000);
|
||||
searchEditText.setHint(dopt.searchHintText);
|
||||
searchEditText.setInputType(dopt.searchInputType == 0 ? searchEditText.getInputType() : dopt.searchInputType);
|
||||
|
||||
searchEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
|
@ -166,9 +187,11 @@ public class SearchOrCustomTextDialog {
|
|||
dialogBuilder.setMessage(dopt.messageText);
|
||||
}
|
||||
dialogBuilder.setView(linearLayout)
|
||||
.setTitle(dopt.titleText)
|
||||
.setOnCancelListener(null)
|
||||
.setNegativeButton(dopt.cancelButtonText, (dialogInterface, i) -> dialogInterface.dismiss());
|
||||
if (dopt.titleText != 0) {
|
||||
dialogBuilder.setTitle(dopt.titleText);
|
||||
}
|
||||
if (dopt.isSearchEnabled) {
|
||||
dialogBuilder.setPositiveButton(dopt.okButtonText, (dialogInterface, i) -> {
|
||||
dialogInterface.dismiss();
|
||||
|
@ -204,7 +227,15 @@ public class SearchOrCustomTextDialog {
|
|||
}
|
||||
dialog.show();
|
||||
if ((w = dialog.getWindow()) != null) {
|
||||
w.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
|
||||
int ds_w = dopt.dialogWidthDp < 100 ? dopt.dialogWidthDp : ((int) (dopt.dialogWidthDp * activity.getResources().getDisplayMetrics().density));
|
||||
int ds_h = dopt.dialogHeightDp < 100 ? dopt.dialogHeightDp : ((int) (dopt.dialogHeightDp * activity.getResources().getDisplayMetrics().density));
|
||||
w.setLayout(ds_w, ds_h);
|
||||
}
|
||||
|
||||
if ((w = dialog.getWindow()) != null && dopt.gravity != Gravity.NO_GRAVITY) {
|
||||
WindowManager.LayoutParams wlp = w.getAttributes();
|
||||
wlp.gravity = dopt.gravity;
|
||||
w.setAttributes(wlp);
|
||||
}
|
||||
|
||||
if (dopt.isSearchEnabled) {
|
||||
|
|
|
@ -12,6 +12,11 @@ package net.gsantner.opoc.util;
|
|||
|
||||
@SuppressWarnings("unused")
|
||||
public class Callback {
|
||||
|
||||
public interface a0 {
|
||||
void callback();
|
||||
}
|
||||
|
||||
public interface a1<A> {
|
||||
void callback(A arg1);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ import android.net.Uri;
|
|||
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.graphics.drawable.VectorDrawableCompat;
|
|||
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,10 +86,11 @@ import java.util.ArrayList;
|
|||
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;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection"})
|
||||
@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources", "UnusedAssignment"})
|
||||
public class ContextUtils {
|
||||
//
|
||||
// Members, Constructors
|
||||
|
@ -117,21 +122,29 @@ public class ContextUtils {
|
|||
*
|
||||
* @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());
|
||||
public int getResId(final ResType resType, final String name) {
|
||||
try {
|
||||
return _context.getResources().getIdentifier(name, resType.name().toLowerCase(), _context.getPackageName());
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get String by given string ressource id (nuermic)
|
||||
*/
|
||||
public String rstr(@StringRes int strResId) {
|
||||
return _context.getString(strResId);
|
||||
public String rstr(@StringRes final int strResId) {
|
||||
try {
|
||||
return _context.getString(strResId);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get String by given string ressource identifier (textual)
|
||||
*/
|
||||
public String rstr(String strResKey) {
|
||||
public String rstr(final String strResKey) {
|
||||
try {
|
||||
return rstr(getResId(ResType.STRING, strResKey));
|
||||
} catch (Resources.NotFoundException e) {
|
||||
|
@ -142,14 +155,22 @@ public class ContextUtils {
|
|||
/**
|
||||
* Get drawable from given ressource identifier
|
||||
*/
|
||||
public Drawable rdrawable(@DrawableRes int resId) {
|
||||
return ContextCompat.getDrawable(_context, resId);
|
||||
public Drawable rdrawable(@DrawableRes final int resId) {
|
||||
try {
|
||||
return ContextCompat.getDrawable(_context, resId);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color by given color ressource id
|
||||
*/
|
||||
public int rcolor(@ColorRes int resId) {
|
||||
public int rcolor(@ColorRes final int resId) {
|
||||
if (resId == 0) {
|
||||
Log.e(getClass().getName(), "ContextUtils::rcolor: resId is 0!");
|
||||
return Color.BLACK;
|
||||
}
|
||||
return ContextCompat.getColor(_context, resId);
|
||||
}
|
||||
|
||||
|
@ -175,12 +196,12 @@ public class ContextUtils {
|
|||
* @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(final int intColor, final 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 + ")";
|
||||
}
|
||||
|
||||
|
@ -205,7 +226,7 @@ public class ContextUtils {
|
|||
src = _context.getPackageManager().getInstallerPackageName(getPackageIdManifest());
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
if (TextUtils.isEmpty(src)) {
|
||||
if (src == null || src.trim().isEmpty()) {
|
||||
return "Sideloaded";
|
||||
} else if (src.toLowerCase().contains(".amazon.")) {
|
||||
return "Amazon Appstore";
|
||||
|
@ -213,7 +234,7 @@ public class ContextUtils {
|
|||
switch (src) {
|
||||
case "com.android.vending":
|
||||
case "com.google.android.feedback": {
|
||||
return "Google Play Store";
|
||||
return "Google Play";
|
||||
}
|
||||
case "org.fdroid.fdroid.privileged":
|
||||
case "org.fdroid.fdroid": {
|
||||
|
@ -237,12 +258,12 @@ public class ContextUtils {
|
|||
* 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);
|
||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
try {
|
||||
Uri uri = Uri.parse(url);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
_context.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +273,7 @@ public class ContextUtils {
|
|||
*/
|
||||
public String getPackageIdManifest() {
|
||||
String pkg = rstr("manifest_package_id");
|
||||
return pkg != null ? pkg : _context.getPackageName();
|
||||
return !TextUtils.isEmpty(pkg) ? pkg : _context.getPackageName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,7 +291,7 @@ public class ContextUtils {
|
|||
* of the package set in manifest (root element).
|
||||
* Falls back to applicationId of the app which may differ from manifest.
|
||||
*/
|
||||
public Object getBuildConfigValue(String fieldName) {
|
||||
public Object getBuildConfigValue(final String fieldName) {
|
||||
String pkg = getPackageIdManifest() + ".BuildConfig";
|
||||
try {
|
||||
Class<?> c = Class.forName(pkg);
|
||||
|
@ -284,9 +305,9 @@ public class ContextUtils {
|
|||
/**
|
||||
* Get a BuildConfig bool value
|
||||
*/
|
||||
public Boolean bcbool(String fieldName, Boolean defaultValue) {
|
||||
public Boolean bcbool(final String fieldName, final Boolean defaultValue) {
|
||||
Object field = getBuildConfigValue(fieldName);
|
||||
if (field != null && field instanceof Boolean) {
|
||||
if (field instanceof Boolean) {
|
||||
return (Boolean) field;
|
||||
}
|
||||
return defaultValue;
|
||||
|
@ -295,9 +316,9 @@ public class ContextUtils {
|
|||
/**
|
||||
* Get a BuildConfig string value
|
||||
*/
|
||||
public String bcstr(String fieldName, String defaultValue) {
|
||||
public String bcstr(final String fieldName, final String defaultValue) {
|
||||
Object field = getBuildConfigValue(fieldName);
|
||||
if (field != null && field instanceof String) {
|
||||
if (field instanceof String) {
|
||||
return (String) field;
|
||||
}
|
||||
return defaultValue;
|
||||
|
@ -306,9 +327,9 @@ public class ContextUtils {
|
|||
/**
|
||||
* Get a BuildConfig string value
|
||||
*/
|
||||
public Integer bcint(String fieldName, int defaultValue) {
|
||||
public Integer bcint(final String fieldName, final int defaultValue) {
|
||||
Object field = getBuildConfigValue(fieldName);
|
||||
if (field != null && field instanceof Integer) {
|
||||
if (field instanceof Integer) {
|
||||
return (Integer) field;
|
||||
}
|
||||
return defaultValue;
|
||||
|
@ -396,8 +417,8 @@ public class ContextUtils {
|
|||
* Check if app with given {@code packageName} is installed
|
||||
*/
|
||||
public boolean isAppInstalled(String packageName) {
|
||||
PackageManager pm = _context.getApplicationContext().getPackageManager();
|
||||
try {
|
||||
PackageManager pm = _context.getApplicationContext().getPackageManager();
|
||||
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
|
@ -409,17 +430,17 @@ 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);
|
||||
Intent intent = new Intent(_context, classToStart);
|
||||
PendingIntent pendi = PendingIntent.getActivity(_context, 555, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
AlarmManager mgr = (AlarmManager) _context.getSystemService(Context.ALARM_SERVICE);
|
||||
if (_context instanceof Activity) {
|
||||
((Activity) _context).finish();
|
||||
}
|
||||
if (mgr != null) {
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, inteP);
|
||||
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendi);
|
||||
} else {
|
||||
inte.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
_context.startActivity(inte);
|
||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
_context.startActivity(intent);
|
||||
}
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
|
@ -488,19 +509,25 @@ public class ContextUtils {
|
|||
* {@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) {
|
||||
public void setAppLanguage(final String androidLC) {
|
||||
Locale locale = getLocaleByAndroidCode(androidLC);
|
||||
locale = (locale != null && !androidLC.isEmpty()) ? locale : Resources.getSystem().getConfiguration().locale;
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
public ContextUtils setLocale(final 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 final int colorOnBottomInt) {
|
||||
return 186 > (((0.299 * Color.red(colorOnBottomInt))
|
||||
+ ((0.587 * Color.green(colorOnBottomInt))
|
||||
+ (0.114 * Color.blue(colorOnBottomInt)))));
|
||||
|
@ -509,7 +536,7 @@ public class ContextUtils {
|
|||
/**
|
||||
* Convert a html string to an android {@link Spanned} object
|
||||
*/
|
||||
public Spanned htmlToSpanned(String html) {
|
||||
public Spanned htmlToSpanned(final String html) {
|
||||
Spanned result;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
|
||||
result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
|
||||
|
@ -568,7 +595,7 @@ public class ContextUtils {
|
|||
return dirs;
|
||||
}
|
||||
|
||||
public String getStorageName(File externalFileDir, boolean storageNameWithoutType) {
|
||||
public String getStorageName(final File externalFileDir, final boolean storageNameWithoutType) {
|
||||
boolean isInt = externalFileDir.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
|
||||
String[] split = externalFileDir.getAbsolutePath().split("/");
|
||||
|
@ -579,7 +606,7 @@ public class ContextUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public List<Pair<File, String>> getStorages(boolean internalStorageFolder, boolean sdcardFolders) {
|
||||
public List<Pair<File, String>> getStorages(final boolean internalStorageFolder, final boolean sdcardFolders) {
|
||||
List<Pair<File, String>> storages = new ArrayList<>();
|
||||
for (Pair<File, String> pair : getAppDataPublicDirs(internalStorageFolder, sdcardFolders, true)) {
|
||||
if (pair.first != null && pair.first.getAbsolutePath().lastIndexOf("/Android/data") > 0) {
|
||||
|
@ -592,7 +619,7 @@ public class ContextUtils {
|
|||
return storages;
|
||||
}
|
||||
|
||||
public File getStorageRootFolder(File file) {
|
||||
public File getStorageRootFolder(final File file) {
|
||||
String filepath;
|
||||
try {
|
||||
filepath = file.getCanonicalPath();
|
||||
|
@ -613,7 +640,7 @@ public class ContextUtils {
|
|||
*
|
||||
* @param files Files and folders to scan
|
||||
*/
|
||||
public void mediaScannerScanFile(File... files) {
|
||||
public void mediaScannerScanFile(final File... files) {
|
||||
if (android.os.Build.VERSION.SDK_INT > 19) {
|
||||
String[] paths = new String[files.length];
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
|
@ -662,8 +689,12 @@ public class ContextUtils {
|
|||
/**
|
||||
* Get a {@link Bitmap} out of a {@link DrawableRes}
|
||||
*/
|
||||
public Bitmap drawableToBitmap(@DrawableRes int drawableId) {
|
||||
return drawableToBitmap(ContextCompat.getDrawable(_context, drawableId));
|
||||
public Bitmap drawableToBitmap(@DrawableRes final int drawableId) {
|
||||
try {
|
||||
return drawableToBitmap(ContextCompat.getDrawable(_context, drawableId));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -671,7 +702,7 @@ public class ContextUtils {
|
|||
* 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(final File imagePath, final int maxDimen) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imagePath.getAbsolutePath(), options);
|
||||
|
@ -687,7 +718,7 @@ public class ContextUtils {
|
|||
* @param maxDimen Max size of the Bitmap (width or height)
|
||||
* @return the scaling factor that needs to be applied to the bitmap
|
||||
*/
|
||||
public int calculateInSampleSize(BitmapFactory.Options options, int maxDimen) {
|
||||
public int calculateInSampleSize(final BitmapFactory.Options options, final int maxDimen) {
|
||||
// Raw height and width of image
|
||||
int height = options.outHeight;
|
||||
int width = options.outWidth;
|
||||
|
@ -703,7 +734,7 @@ public class ContextUtils {
|
|||
* 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(final Bitmap bitmap, final int maxDimen) {
|
||||
int picSize = Math.min(bitmap.getHeight(), bitmap.getWidth());
|
||||
float scale = 1.f * maxDimen / picSize;
|
||||
Matrix matrix = new Matrix();
|
||||
|
@ -714,7 +745,7 @@ public class ContextUtils {
|
|||
/**
|
||||
* Write the given {@link Bitmap} to {@code imageFile}, in {@link CompressFormat#JPEG} format
|
||||
*/
|
||||
public boolean writeImageToFileJpeg(File imageFile, Bitmap image) {
|
||||
public boolean writeImageToFileJpeg(final File imageFile, final Bitmap image) {
|
||||
return writeImageToFile(imageFile, image, Bitmap.CompressFormat.JPEG, 95);
|
||||
}
|
||||
|
||||
|
@ -727,7 +758,7 @@ public class ContextUtils {
|
|||
* @param quality Quality level, defaults to 95
|
||||
* @return True if writing was successful
|
||||
*/
|
||||
public boolean writeImageToFile(File targetFile, Bitmap image, CompressFormat format, Integer quality) {
|
||||
public boolean writeImageToFile(final File targetFile, final Bitmap image, CompressFormat format, Integer quality) {
|
||||
File folder = new File(targetFile.getParent());
|
||||
if (quality == null || quality < 0 || quality > 100) {
|
||||
quality = 95;
|
||||
|
@ -765,7 +796,7 @@ public class ContextUtils {
|
|||
* 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) {
|
||||
public Bitmap drawTextOnDrawable(@DrawableRes final int drawableRes, final String text, final int textSize) {
|
||||
Resources resources = _context.getResources();
|
||||
float scale = resources.getDisplayMetrics().density;
|
||||
Bitmap bitmap = drawableToBitmap(drawableRes);
|
||||
|
@ -790,7 +821,7 @@ public class ContextUtils {
|
|||
* 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) {
|
||||
public void tintMenuItems(final Menu menu, final boolean recurse, @ColorInt final int iconColor) {
|
||||
for (int i = 0; i < menu.size(); i++) {
|
||||
MenuItem item = menu.getItem(i);
|
||||
try {
|
||||
|
@ -807,14 +838,14 @@ 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 final int drawableRes, @ColorInt final int 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 final int color) {
|
||||
if (drawable != null) {
|
||||
drawable = DrawableCompat.wrap(drawable);
|
||||
DrawableCompat.setTint(drawable.mutate(), color);
|
||||
|
@ -826,7 +857,10 @@ public class ContextUtils {
|
|||
* 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(final Menu menu, final 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);
|
||||
|
@ -886,7 +920,7 @@ public class ContextUtils {
|
|||
}
|
||||
|
||||
|
||||
public String getMimeType(File file) {
|
||||
public String getMimeType(final File file) {
|
||||
return getMimeType(Uri.fromFile(file));
|
||||
}
|
||||
|
||||
|
@ -895,7 +929,7 @@ public class ContextUtils {
|
|||
* Android/Java's own MimeType map is very very small and detection barely works at all
|
||||
* Hence use custom map for some file extensions
|
||||
*/
|
||||
public String getMimeType(Uri uri) {
|
||||
public String getMimeType(final Uri uri) {
|
||||
String mimeType = null;
|
||||
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
|
||||
ContentResolver cr = _context.getContentResolver();
|
||||
|
@ -936,7 +970,7 @@ public class ContextUtils {
|
|||
return mimeType;
|
||||
}
|
||||
|
||||
public Integer parseColor(String colorstr) {
|
||||
public Integer parseColor(final String colorstr) {
|
||||
if (colorstr == null || colorstr.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -957,6 +991,22 @@ public class ContextUtils {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Vibrate device one time by given amount of time, defaulting to 50ms
|
||||
// Requires <uses-permission android:name="android.permission.VIBRATE" /> in AndroidManifest to work
|
||||
@SuppressWarnings("UnnecessaryReturnStatement")
|
||||
@SuppressLint("MissingPermission")
|
||||
public void vibrate(final 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.io.InputStreamReader;
|
|||
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 class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
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 class FileUtils {
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ public class NetworkUtils {
|
|||
int written = 0;
|
||||
final float invLength = 1f / connection.getContentLength();
|
||||
|
||||
byte data[] = new byte[BUFFER_SIZE];
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
while ((count = input.read(data)) != -1) {
|
||||
output.write(data, 0, count);
|
||||
if (invLength != -1f && progressCallback != null) {
|
||||
|
|
|
@ -96,12 +96,12 @@ public class ShareUtil {
|
|||
protected String _fileProviderAuthority;
|
||||
protected String _chooserTitle;
|
||||
|
||||
public ShareUtil(Context context) {
|
||||
public ShareUtil(final Context context) {
|
||||
_context = context;
|
||||
_chooserTitle = "➥";
|
||||
}
|
||||
|
||||
public void setContext(Context c) {
|
||||
public void setContext(final Context c) {
|
||||
_context = c;
|
||||
}
|
||||
|
||||
|
@ -116,13 +116,13 @@ public class ShareUtil {
|
|||
return _fileProviderAuthority;
|
||||
}
|
||||
|
||||
public ShareUtil setFileProviderAuthority(String fileProviderAuthority) {
|
||||
public ShareUtil setFileProviderAuthority(final String fileProviderAuthority) {
|
||||
_fileProviderAuthority = fileProviderAuthority;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ShareUtil setChooserTitle(String title) {
|
||||
public ShareUtil setChooserTitle(final String title) {
|
||||
_chooserTitle = title;
|
||||
return this;
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ public class ShareUtil {
|
|||
* @param file the file
|
||||
* @return Uri for this file
|
||||
*/
|
||||
public Uri getUriByFileProviderAuthority(File file) {
|
||||
public Uri getUriByFileProviderAuthority(final File file) {
|
||||
return FileProvider.getUriForFile(_context, getFileProviderAuthority(), file);
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ public class ShareUtil {
|
|||
* @param intent Thing to be shared
|
||||
* @param chooserText The title text for the chooser, or null for default
|
||||
*/
|
||||
public void showChooser(Intent intent, String chooserText) {
|
||||
public void showChooser(final Intent intent, final String chooserText) {
|
||||
_context.startActivity(Intent.createChooser(intent,
|
||||
chooserText != null ? chooserText : _chooserTitle));
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ public class ShareUtil {
|
|||
* @param iconRes Icon resource for the item
|
||||
* @param title Title of the item
|
||||
*/
|
||||
public void createLauncherDesktopShortcut(Intent intent, @DrawableRes int iconRes, String title) {
|
||||
public void createLauncherDesktopShortcut(final Intent intent, @DrawableRes final int iconRes, final String title) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
if (intent.getAction() == null) {
|
||||
|
@ -182,7 +182,7 @@ public class ShareUtil {
|
|||
* @param iconRes Icon resource for the item
|
||||
* @param title Title of the item
|
||||
*/
|
||||
public void createLauncherDesktopShortcutLegacy(Intent intent, @DrawableRes int iconRes, String title) {
|
||||
public void createLauncherDesktopShortcutLegacy(final Intent intent, @DrawableRes final int iconRes, final String title) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
if (intent.getAction() == null) {
|
||||
|
@ -203,7 +203,7 @@ public class ShareUtil {
|
|||
* @param text The text to share
|
||||
* @param mimeType MimeType or null (uses text/plain)
|
||||
*/
|
||||
public void shareText(String text, @Nullable String mimeType) {
|
||||
public void shareText(final String text, @Nullable final String mimeType) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, text);
|
||||
intent.setType(mimeType != null ? mimeType : MIME_TEXT_PLAIN);
|
||||
|
@ -216,7 +216,7 @@ public class ShareUtil {
|
|||
* @param file The file to share
|
||||
* @param mimeType The files mime type
|
||||
*/
|
||||
public boolean shareStream(File file, String mimeType) {
|
||||
public boolean shareStream(final File file, final String mimeType) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.putExtra(EXTRA_FILEPATH, file.getAbsolutePath());
|
||||
intent.setType(mimeType);
|
||||
|
@ -237,7 +237,7 @@ public class ShareUtil {
|
|||
* @param files The files to share
|
||||
* @param mimeType The files mime type. Usally * / * is the best option
|
||||
*/
|
||||
public boolean shareStreamMultiple(Collection<File> files, String mimeType) {
|
||||
public boolean shareStreamMultiple(final Collection<File> files, final String mimeType) {
|
||||
ArrayList<Uri> uris = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
File uri = new File(file.toString());
|
||||
|
@ -258,14 +258,13 @@ public class ShareUtil {
|
|||
/**
|
||||
* Start calendar application to add new event, with given details prefilled
|
||||
*/
|
||||
public boolean createCalendarAppointment(@Nullable String title, @Nullable String description, @Nullable String location, @Nullable Long... startAndEndTime) {
|
||||
public boolean createCalendarAppointment(@Nullable final String title, @Nullable final String description, @Nullable final String location, @Nullable final Long... startAndEndTime) {
|
||||
Intent intent = new Intent(Intent.ACTION_INSERT).setData(CalendarContract.Events.CONTENT_URI);
|
||||
if (title != null) {
|
||||
intent.putExtra(CalendarContract.Events.TITLE, title);
|
||||
}
|
||||
if (description != null) {
|
||||
description = description.length() > 800 ? description.substring(0, 800) : description;
|
||||
intent.putExtra(CalendarContract.Events.DESCRIPTION, description);
|
||||
intent.putExtra(CalendarContract.Events.DESCRIPTION, (description.length() > 800 ? description.substring(0, 800) : description));
|
||||
}
|
||||
if (location != null) {
|
||||
intent.putExtra(CalendarContract.Events.EVENT_LOCATION, location);
|
||||
|
@ -292,7 +291,7 @@ public class ShareUtil {
|
|||
*
|
||||
* @param file The file to share
|
||||
*/
|
||||
public boolean viewFileInOtherApp(File file, @Nullable String type) {
|
||||
public boolean viewFileInOtherApp(final File file, @Nullable final String type) {
|
||||
// On some specific devices the first won't work
|
||||
Uri fileUri = null;
|
||||
try {
|
||||
|
@ -324,7 +323,7 @@ public class ShareUtil {
|
|||
* @param format A {@link Bitmap.CompressFormat}, supporting JPEG,PNG,WEBP
|
||||
* @return if success, true
|
||||
*/
|
||||
public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format) {
|
||||
public boolean shareImage(final Bitmap bitmap, final Bitmap.CompressFormat format) {
|
||||
return shareImage(bitmap, format, 95, "SharedImage");
|
||||
}
|
||||
|
||||
|
@ -337,7 +336,7 @@ public class ShareUtil {
|
|||
* @param quality Quality of the exported image [0-100]
|
||||
* @return if success, true
|
||||
*/
|
||||
public boolean shareImage(Bitmap bitmap, Bitmap.CompressFormat format, int quality, String imageName) {
|
||||
public boolean shareImage(final Bitmap bitmap, final Bitmap.CompressFormat format, final int quality, final String imageName) {
|
||||
try {
|
||||
String ext = format.name().toLowerCase();
|
||||
File file = File.createTempFile(imageName, "." + ext.replace("jpeg", "jpg"), _context.getExternalCacheDir());
|
||||
|
@ -359,19 +358,23 @@ public class ShareUtil {
|
|||
* @return {{@link PrintJob}} or null
|
||||
*/
|
||||
@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) {
|
||||
}
|
||||
}
|
||||
|
@ -386,8 +389,7 @@ public class ShareUtil {
|
|||
* See {@link #print(WebView, String) print method}
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
@SuppressWarnings("deprecation")
|
||||
public PrintJob createPdf(WebView webview, String jobName) {
|
||||
public PrintJob createPdf(final WebView webview, final String jobName) {
|
||||
return print(webview, jobName);
|
||||
}
|
||||
|
||||
|
@ -399,7 +401,7 @@ public class ShareUtil {
|
|||
* @return A {@link Bitmap} or null
|
||||
*/
|
||||
@Nullable
|
||||
public static Bitmap getBitmapFromWebView(WebView webView) {
|
||||
public static Bitmap getBitmapFromWebView(final WebView webView) {
|
||||
try {
|
||||
//Measure WebView's content
|
||||
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
|
@ -432,7 +434,7 @@ public class ShareUtil {
|
|||
* Replace (primary) clipboard contents with given {@code text}
|
||||
* @param text Text to be set
|
||||
*/
|
||||
public boolean setClipboard(CharSequence text) {
|
||||
public boolean setClipboard(final CharSequence text) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||
android.text.ClipboardManager cm = ((android.text.ClipboardManager) _context.getSystemService(Context.CLIPBOARD_SERVICE));
|
||||
if (cm != null) {
|
||||
|
@ -485,7 +487,7 @@ public class ShareUtil {
|
|||
* @param callback Callback after paste try
|
||||
* @param serverOrNothing Supply one or no hastebin server. If empty, the default gets taken
|
||||
*/
|
||||
public void pasteOnHastebin(final String text, final Callback.a2<Boolean, String> callback, String... serverOrNothing) {
|
||||
public void pasteOnHastebin(final String text, final Callback.a2<Boolean, String> callback, final String... serverOrNothing) {
|
||||
final Handler handler = new Handler();
|
||||
final String server = (serverOrNothing != null && serverOrNothing.length > 0 && serverOrNothing[0] != null)
|
||||
? serverOrNothing[0] : "https://hastebin.com";
|
||||
|
@ -507,7 +509,7 @@ public class ShareUtil {
|
|||
* @param body Body (content) text to be prefilled in the mail
|
||||
* @param to recipients to be prefilled in the mail
|
||||
*/
|
||||
public void draftEmail(String subject, String body, String... to) {
|
||||
public void draftEmail(final String subject, final String body, final String... to) {
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setData(Uri.parse("mailto:"));
|
||||
if (subject != null) {
|
||||
|
@ -528,7 +530,7 @@ public class ShareUtil {
|
|||
* @param receivingIntent The intent from {@link Activity#getIntent()}
|
||||
* @return A file or null if extraction did not succeed
|
||||
*/
|
||||
public File extractFileFromIntent(Intent receivingIntent) {
|
||||
public File extractFileFromIntent(final Intent receivingIntent) {
|
||||
String action = receivingIntent.getAction();
|
||||
String type = receivingIntent.getType();
|
||||
File tmpf;
|
||||
|
@ -572,6 +574,14 @@ public class ShareUtil {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 +597,16 @@ public class ShareUtil {
|
|||
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 +644,11 @@ public class ShareUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public String extractFileFromIntentStr(final 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)}.
|
||||
|
@ -634,7 +659,8 @@ public class ShareUtil {
|
|||
*
|
||||
* @param target Path to file to write to, if folder the filename gets app_name + millis + random filename. If null DCIM folder is used.
|
||||
*/
|
||||
public String requestCameraPicture(File target) {
|
||||
@SuppressWarnings("RegExpRedundantEscape")
|
||||
public String requestCameraPicture(final File target) {
|
||||
if (!(_context instanceof Activity)) {
|
||||
throw new RuntimeException("Error: ShareUtil.requestCameraPicture needs an Activity Context.");
|
||||
}
|
||||
|
@ -647,7 +673,7 @@ public class ShareUtil {
|
|||
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");
|
||||
|
@ -686,7 +712,7 @@ public class ShareUtil {
|
|||
* Also may forward results via local broadcast
|
||||
*/
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public Object extractResultFromActivityResult(int requestCode, int resultCode, Intent data, Activity... activityOrNull) {
|
||||
public Object extractResultFromActivityResult(final int requestCode, final int resultCode, final Intent data, final Activity... activityOrNull) {
|
||||
Activity activity = greedyGetActivity(activityOrNull);
|
||||
switch (requestCode) {
|
||||
case REQUEST_CAMERA_PICTURE: {
|
||||
|
@ -717,6 +743,10 @@ public class ShareUtil {
|
|||
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 {
|
||||
|
@ -762,7 +792,7 @@ public class ShareUtil {
|
|||
* Send a local broadcast (to receive within app), with given action and string-extra+value.
|
||||
* This is a convenience method for quickly sending just one thing.
|
||||
*/
|
||||
public void sendLocalBroadcastWithStringExtra(String action, String extra, CharSequence value) {
|
||||
public void sendLocalBroadcastWithStringExtra(final String action, final String extra, final CharSequence value) {
|
||||
Intent intent = new Intent(action);
|
||||
intent.putExtra(extra, value);
|
||||
LocalBroadcastManager.getInstance(_context).sendBroadcast(intent);
|
||||
|
@ -776,7 +806,7 @@ public class ShareUtil {
|
|||
* @param filterActions All {@link IntentFilter} actions to filter for
|
||||
* @return The created instance. Has to be unregistered on {@link Activity} lifecycle events.
|
||||
*/
|
||||
public BroadcastReceiver receiveResultFromLocalBroadcast(Callback.a2<Intent, BroadcastReceiver> callback, boolean autoUnregister, String... filterActions) {
|
||||
public BroadcastReceiver receiveResultFromLocalBroadcast(final Callback.a2<Intent, BroadcastReceiver> callback, final boolean autoUnregister, final String... filterActions) {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
for (String filterAction : filterActions) {
|
||||
intentFilter.addAction(filterAction);
|
||||
|
@ -804,7 +834,7 @@ public class ShareUtil {
|
|||
*
|
||||
* @param file File that should be edited
|
||||
*/
|
||||
public void requestPictureEdit(File file) {
|
||||
public void requestPictureEdit(final File file) {
|
||||
Uri uri = getUriByFileProviderAuthority(file);
|
||||
int flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
|
||||
|
@ -826,9 +856,10 @@ public class ShareUtil {
|
|||
*
|
||||
* @param file Target file
|
||||
* @param mode 1 for picture, 2 for video, anything else for other
|
||||
* @return
|
||||
* @return Media URI
|
||||
*/
|
||||
public Uri getMediaUri(File file, int mode) {
|
||||
@SuppressWarnings("TryFinallyCanBeTryWithResources")
|
||||
public Uri getMediaUri(final File file, final int mode) {
|
||||
Uri uri = MediaStore.Files.getContentUri("external");
|
||||
uri = (mode != 0) ? (mode == 1 ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI) : uri;
|
||||
|
||||
|
@ -854,7 +885,7 @@ public class ShareUtil {
|
|||
* which implement the Chrome Custom Tab interface. This method changes
|
||||
* the customtab intent to use an available compatible browser, if available.
|
||||
*/
|
||||
public void enableChromeCustomTabsForOtherBrowsers(Intent customTabIntent) {
|
||||
public void enableChromeCustomTabsForOtherBrowsers(final Intent customTabIntent) {
|
||||
String[] checkpkgs = new String[]{
|
||||
"com.android.chrome", "com.chrome.beta", "com.chrome.dev", "com.google.android.apps.chrome", "org.chromium.chrome",
|
||||
"org.mozilla.fennec_fdroid", "org.mozilla.firefox", "org.mozilla.firefox_beta", "org.mozilla.fennec_aurora",
|
||||
|
@ -905,7 +936,7 @@ public class ShareUtil {
|
|||
* Request storage access. The user needs to press "Select storage" at the correct storage.
|
||||
* @param activity The activity which will receive the result from startActivityForResult
|
||||
*/
|
||||
public void requestStorageAccessFramework(Activity... activity) {
|
||||
public void requestStorageAccessFramework(final Activity... activity) {
|
||||
Activity a = greedyGetActivity(activity);
|
||||
if (a != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
|
@ -961,8 +992,12 @@ public class ShareUtil {
|
|||
* @param file The file object (file/folder)
|
||||
* @return Wether or not the file is under storage access folder
|
||||
*/
|
||||
public boolean isUnderStorageAccessFolder(File file) {
|
||||
public boolean isUnderStorageAccessFolder(final File file) {
|
||||
if (file != null) {
|
||||
// When file writeable as is, it's the fastest way to learn SAF isn't required
|
||||
if (file.canWrite()) {
|
||||
return false;
|
||||
}
|
||||
ContextUtils cu = new ContextUtils(_context);
|
||||
for (Pair<File, String> storage : cu.getStorages(false, true)) {
|
||||
if (file.getAbsolutePath().startsWith(storage.first.getAbsolutePath())) {
|
||||
|
@ -978,7 +1013,7 @@ public class ShareUtil {
|
|||
/**
|
||||
* Greedy extract Activity from parameter or convert context if it's a activity
|
||||
*/
|
||||
private Activity greedyGetActivity(Activity... activity) {
|
||||
private Activity greedyGetActivity(final Activity... activity) {
|
||||
if (activity != null && activity.length != 0 && activity[0] != null) {
|
||||
return activity[0];
|
||||
}
|
||||
|
@ -996,10 +1031,11 @@ public class ShareUtil {
|
|||
* @param isDir Wether or not the given file parameter is a directory
|
||||
* @return Wether or not the file can be written
|
||||
*/
|
||||
public boolean canWriteFile(File file, boolean isDir) {
|
||||
public boolean canWriteFile(final File file, final 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 {
|
||||
|
@ -1017,7 +1053,8 @@ public class ShareUtil {
|
|||
* @param isDir Wether or not file is a directory. For non-existing (to be created) files this info is not known hence required.
|
||||
* @return A {@link DocumentFile} object or null if file cannot be converted
|
||||
*/
|
||||
public DocumentFile getDocumentFile(File file, boolean isDir) {
|
||||
@SuppressWarnings("RegExpRedundantEscape")
|
||||
public DocumentFile getDocumentFile(final File file, final boolean isDir) {
|
||||
// On older versions use fromFile
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
||||
return DocumentFile.fromFile(file);
|
||||
|
@ -1066,7 +1103,7 @@ public class ShareUtil {
|
|||
return dof;
|
||||
}
|
||||
|
||||
public void showMountSdDialog(@StringRes int title, @StringRes int description, @DrawableRes int mountDescriptionGraphic, Activity... activityOrNull) {
|
||||
public void showMountSdDialog(@StringRes final int title, @StringRes final int description, @DrawableRes final int mountDescriptionGraphic, final Activity... activityOrNull) {
|
||||
Activity activity = greedyGetActivity(activityOrNull);
|
||||
if (activity == null) {
|
||||
return;
|
||||
|
@ -1087,11 +1124,12 @@ public class ShareUtil {
|
|||
dialogi.show();
|
||||
}
|
||||
|
||||
public void writeFile(File file, boolean isDirectory, Callback.a2<Boolean, FileOutputStream> writeFileCallback) {
|
||||
@SuppressWarnings({"ResultOfMethodCallIgnored", "StatementWithEmptyBody"})
|
||||
public void writeFile(final File file, final boolean isDirectory, final Callback.a2<Boolean, FileOutputStream> 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 +1150,10 @@ public class ShareUtil {
|
|||
writeFileCallback.callback(fileOutputStream != null || (isDirectory && file.exists()), fileOutputStream);
|
||||
}
|
||||
if (fileOutputStream != null) {
|
||||
fileOutputStream.close();
|
||||
try {
|
||||
fileOutputStream.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (pfd != null) {
|
||||
pfd.close();
|
||||
|
@ -1132,7 +1173,7 @@ public class ShareUtil {
|
|||
* @param directCall Direct call number if possible
|
||||
*/
|
||||
@SuppressWarnings("SimplifiableConditionalExpression")
|
||||
public void callTelephoneNumber(String telNo, boolean... directCall) {
|
||||
public void callTelephoneNumber(final String telNo, final boolean... directCall) {
|
||||
Activity activity = greedyGetActivity();
|
||||
if (activity == null) {
|
||||
throw new RuntimeException("Error: ShareUtil::callTelephoneNumber needs to be contstructed with activity context");
|
||||
|
|
|
@ -13,8 +13,8 @@ import java.text.SimpleDateFormat
|
|||
|
||||
buildscript {
|
||||
ext {
|
||||
version_gradle_tools = "3.5.1"
|
||||
version_plugin_kotlin = "1.3.50"
|
||||
version_gradle_tools = "3.5.2"
|
||||
version_plugin_kotlin = "1.3.60"
|
||||
enable_plugin_kotlin = false
|
||||
|
||||
version_compileSdk = 28
|
||||
|
|
Loading…
Reference in a new issue