* author: Blankj
@@ -23,6 +24,7 @@ public abstract class BaseItem {
private static final SparseIntArray LAYOUT_SPARSE_ARRAY = new SparseIntArray();
private static final SparseArray VIEW_SPARSE_ARRAY = new SparseArray<>();
+ public boolean isBindViewHolder = false;
static ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
int layoutByType = LAYOUT_SPARSE_ARRAY.get(viewType, -1);
@@ -38,7 +40,11 @@ static ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType
public abstract void bind(@NonNull final ItemViewHolder holder, final int position);
+ public void partialUpdate(List
* author: Blankj
@@ -31,10 +32,9 @@ public final class BusUtils {
private static final Object NULL = "nULl";
private static final String TAG = "BusUtils";
- private final Map> mTag_BusInfoListMap = new HashMap<>();
-
+ private final Map> mTag_BusInfoListMap = new ConcurrentHashMap<>();
private final Map> mClassName_BusesMap = new ConcurrentHashMap<>();
- private final Map> mClassName_TagsMap = new HashMap<>();
+ private final Map> mClassName_TagsMap = new ConcurrentHashMap<>();
private final Map> mClassName_Tag_Arg4StickyMap = new ConcurrentHashMap<>();
private BusUtils() {
@@ -58,33 +58,33 @@ private void registerBus(String tag,
boolean sticky, String threadMode, int priority) {
List busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
- busInfoList = new ArrayList<>();
+ busInfoList = new CopyOnWriteArrayList<>();
mTag_BusInfoListMap.put(tag, busInfoList);
}
- busInfoList.add(new BusInfo(className, funName, paramType, paramName, sticky, threadMode, priority));
+ busInfoList.add(new BusInfo(tag, className, funName, paramType, paramName, sticky, threadMode, priority));
}
- public static void register(final Object bus) {
+ public static void register(@Nullable final Object bus) {
getInstance().registerInner(bus);
}
- public static void unregister(final Object bus) {
+ public static void unregister(@Nullable final Object bus) {
getInstance().unregisterInner(bus);
}
- public static void post(final String tag) {
+ public static void post(@NonNull final String tag) {
post(tag, NULL);
}
- public static void post(final String tag, final Object arg) {
+ public static void post(@NonNull final String tag, @NonNull final Object arg) {
getInstance().postInner(tag, arg);
}
- public static void postSticky(final String tag) {
+ public static void postSticky(@NonNull final String tag) {
postSticky(tag, NULL);
}
- public static void postSticky(final String tag, final Object arg) {
+ public static void postSticky(@NonNull final String tag, final Object arg) {
getInstance().postStickyInner(tag, arg);
}
@@ -105,24 +105,38 @@ private static BusUtils getInstance() {
return LazyHolder.INSTANCE;
}
- private void registerInner(final Object bus) {
+ private void registerInner(@Nullable final Object bus) {
if (bus == null) return;
- Class aClass = bus.getClass();
+ Class> aClass = bus.getClass();
String className = aClass.getName();
+ boolean isNeedRecordTags = false;
synchronized (mClassName_BusesMap) {
Set buses = mClassName_BusesMap.get(className);
if (buses == null) {
buses = new CopyOnWriteArraySet<>();
mClassName_BusesMap.put(className, buses);
+ isNeedRecordTags = true;
}
- buses.add(bus);
+ if (buses.contains(bus)) {
+ Log.w(TAG, "The bus of <" + bus + "> already registered.");
+ return;
+ } else {
+ buses.add(bus);
+ }
+ }
+ if (isNeedRecordTags) {
+ recordTags(aClass, className);
}
+ consumeStickyIfExist(bus);
+ }
+
+ private void recordTags(Class> aClass, String className) {
List tags = mClassName_TagsMap.get(className);
if (tags == null) {
synchronized (mClassName_TagsMap) {
tags = mClassName_TagsMap.get(className);
if (tags == null) {
- tags = new ArrayList<>();
+ tags = new CopyOnWriteArrayList<>();
for (Map.Entry> entry : mTag_BusInfoListMap.entrySet()) {
for (BusInfo busInfo : entry.getValue()) {
try {
@@ -139,15 +153,38 @@ private void registerInner(final Object bus) {
}
}
}
- processSticky(bus);
}
- private void processSticky(final Object bus) {
+ private void consumeStickyIfExist(final Object bus) {
Map tagArgMap = mClassName_Tag_Arg4StickyMap.get(bus.getClass().getName());
if (tagArgMap == null) return;
synchronized (mClassName_Tag_Arg4StickyMap) {
for (Map.Entry tagArgEntry : tagArgMap.entrySet()) {
- postInner(tagArgEntry.getKey(), tagArgEntry.getValue());
+ consumeSticky(bus, tagArgEntry.getKey(), tagArgEntry.getValue());
+ }
+ }
+ }
+
+ private void consumeSticky(final Object bus, final String tag, final Object arg) {
+ List busInfoList = mTag_BusInfoListMap.get(tag);
+ if (busInfoList == null) {
+ Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
+ return;
+ }
+ for (BusInfo busInfo : busInfoList) {
+ if (!busInfo.subClassNames.contains(bus.getClass().getName())) {
+ continue;
+ }
+ if (!busInfo.sticky) {
+ continue;
+ }
+
+ synchronized (mClassName_Tag_Arg4StickyMap) {
+ Map tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
+ if (tagArgMap == null || !tagArgMap.containsKey(tag)) {
+ continue;
+ }
+ invokeBus(bus, arg, busInfo, true);
}
}
}
@@ -173,18 +210,29 @@ private void postInner(final String tag, final Object arg, final boolean sticky)
List busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
+ if (mTag_BusInfoListMap.isEmpty()) {
+ Log.e(TAG, "Please check whether the bus plugin is applied.");
+ }
return;
}
for (BusInfo busInfo : busInfoList) {
- if (busInfo.method == null) {
- Method method = getMethodByBusInfo(busInfo);
- if (method == null) {
- return;
- }
- busInfo.method = method;
+ invokeBus(arg, busInfo, sticky);
+ }
+ }
+
+ private void invokeBus(Object arg, BusInfo busInfo, boolean sticky) {
+ invokeBus(null, arg, busInfo, sticky);
+ }
+
+ private void invokeBus(Object bus, Object arg, BusInfo busInfo, boolean sticky) {
+ if (busInfo.method == null) {
+ Method method = getMethodByBusInfo(busInfo);
+ if (method == null) {
+ return;
}
- invokeMethod(tag, arg, busInfo, sticky);
+ busInfo.method = method;
}
+ invokeMethod(bus, arg, busInfo, sticky);
}
private Method getMethodByBusInfo(BusInfo busInfo) {
@@ -225,11 +273,15 @@ private Class getClassName(String paramType) throws ClassNotFoundException {
}
}
- private void invokeMethod(final String tag, final Object arg, final BusInfo busInfo, final boolean sticky) {
+ private void invokeMethod(final Object arg, final BusInfo busInfo, final boolean sticky) {
+ invokeMethod(null, arg, busInfo, sticky);
+ }
+
+ private void invokeMethod(final Object bus, final Object arg, final BusInfo busInfo, final boolean sticky) {
Runnable runnable = new Runnable() {
@Override
public void run() {
- realInvokeMethod(tag, arg, busInfo, sticky);
+ realInvokeMethod(bus, arg, busInfo, sticky);
}
};
switch (busInfo.threadMode) {
@@ -253,22 +305,28 @@ public void run() {
}
}
- private void realInvokeMethod(final String tag, Object arg, BusInfo busInfo, boolean sticky) {
+ private void realInvokeMethod(Object bus, Object arg, BusInfo busInfo, boolean sticky) {
Set buses = new HashSet<>();
- for (String subClassName : busInfo.subClassNames) {
- Set subBuses = mClassName_BusesMap.get(subClassName);
- if (subBuses != null && !subBuses.isEmpty()) {
- buses.addAll(subBuses);
+ if (bus == null) {
+ for (String subClassName : busInfo.subClassNames) {
+ Set subBuses = mClassName_BusesMap.get(subClassName);
+ if (subBuses != null && !subBuses.isEmpty()) {
+ buses.addAll(subBuses);
+ }
}
- }
- if (buses.size() == 0) {
- if (!sticky) {
- Log.e(TAG, "The bus of tag <" + tag + "> was not registered before.");
- return;
- } else {
+ if (buses.size() == 0) {
+ if (!sticky) {
+ Log.e(TAG, "The " + busInfo + " was not registered before.");
+ }
return;
}
+ } else {
+ buses.add(bus);
}
+ invokeBuses(arg, busInfo, buses);
+ }
+
+ private void invokeBuses(Object arg, BusInfo busInfo, Set buses) {
try {
if (arg == NULL) {
for (Object bus : buses) {
@@ -292,20 +350,21 @@ private void postStickyInner(final String tag, final Object arg) {
Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
return;
}
+ // 获取多对象,然后消费各个 busInfoList
for (BusInfo busInfo : busInfoList) {
if (!busInfo.sticky) { // not sticky bus will post directly.
- postInner(tag, arg);
- return;
+ invokeBus(arg, busInfo, false);
+ continue;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null) {
- tagArgMap = new HashMap<>();
+ tagArgMap = new ConcurrentHashMap<>();
mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);
}
tagArgMap.put(tag, arg);
}
- postInner(tag, arg, true);
+ invokeBus(arg, busInfo, true);
}
}
@@ -317,13 +376,11 @@ private void removeStickyInner(final String tag) {
}
for (BusInfo busInfo : busInfoList) {
if (!busInfo.sticky) {
- Log.e(TAG, "The bus of tag <" + tag + "> is not sticky.");
- return;
+ continue;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null || !tagArgMap.containsKey(tag)) {
- Log.e(TAG, "The sticky bus of tag <" + tag + "> didn't post.");
return;
}
tagArgMap.remove(tag);
@@ -339,6 +396,7 @@ static void registerBus4Test(String tag,
private static final class BusInfo {
+ String tag;
String className;
String funName;
String paramType;
@@ -349,8 +407,9 @@ private static final class BusInfo {
Method method;
List subClassNames;
- BusInfo(String className, String funName, String paramType, String paramName,
+ BusInfo(String tag, String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode, int priority) {
+ this.tag = tag;
this.className = className;
this.funName = funName;
this.paramType = paramType;
@@ -363,14 +422,19 @@ private static final class BusInfo {
@Override
public String toString() {
- return "BusInfo { desc: " + className + "#" + funName +
- ("".equals(paramType) ? "()" : ("(" + paramType + " " + paramName + ")")) +
+ return "BusInfo { tag : " + tag +
+ ", desc: " + getDesc() +
", sticky: " + sticky +
", threadMode: " + threadMode +
", method: " + method +
", priority: " + priority +
" }";
}
+
+ private String getDesc() {
+ return className + "#" + funName +
+ ("".equals(paramType) ? "()" : ("(" + paramType + " " + paramName + ")"));
+ }
}
public enum ThreadMode {
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java
index bb48a4b5cf..23100f60b0 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java
@@ -3,13 +3,15 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.support.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.Serializable;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
*
* author: Blankj
@@ -27,7 +29,7 @@ public final class CacheDiskStaticUtils {
*
* @param cacheDiskUtils The default instance of {@link CacheDiskUtils}.
*/
- public static void setDefaultCacheDiskUtils(final CacheDiskUtils cacheDiskUtils) {
+ public static void setDefaultCacheDiskUtils(@Nullable final CacheDiskUtils cacheDiskUtils) {
sDefaultCacheDiskUtils = cacheDiskUtils;
}
@@ -37,7 +39,7 @@ public static void setDefaultCacheDiskUtils(final CacheDiskUtils cacheDiskUtils)
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final byte[] value) {
+ public static void put(@NonNull final String key, @Nullable final byte[] value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -48,7 +50,7 @@ public static void put(@NonNull final String key, final byte[] value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final byte[] value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final byte[] value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -69,7 +71,7 @@ public static byte[] getBytes(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the bytes if cache exists or defaultValue otherwise
*/
- public static byte[] getBytes(@NonNull final String key, final byte[] defaultValue) {
+ public static byte[] getBytes(@NonNull final String key, @Nullable final byte[] defaultValue) {
return getBytes(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -83,7 +85,7 @@ public static byte[] getBytes(@NonNull final String key, final byte[] defaultVal
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final String value) {
+ public static void put(@NonNull final String key, @Nullable final String value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -94,7 +96,7 @@ public static void put(@NonNull final String key, final String value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final String value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final String value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -115,7 +117,7 @@ public static String getString(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the string value if cache exists or defaultValue otherwise
*/
- public static String getString(@NonNull final String key, final String defaultValue) {
+ public static String getString(@NonNull final String key, @Nullable final String defaultValue) {
return getString(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -129,7 +131,7 @@ public static String getString(@NonNull final String key, final String defaultVa
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final JSONObject value) {
+ public static void put(@NonNull final String key, @Nullable final JSONObject value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -141,7 +143,7 @@ public static void put(@NonNull final String key, final JSONObject value) {
* @param saveTime The save time of cache, in seconds.
*/
public static void put(@NonNull final String key,
- final JSONObject value,
+ @Nullable final JSONObject value,
final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -163,7 +165,7 @@ public static JSONObject getJSONObject(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the JSONObject if cache exists or defaultValue otherwise
*/
- public static JSONObject getJSONObject(@NonNull final String key, final JSONObject defaultValue) {
+ public static JSONObject getJSONObject(@NonNull final String key, @Nullable final JSONObject defaultValue) {
return getJSONObject(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -178,7 +180,7 @@ public static JSONObject getJSONObject(@NonNull final String key, final JSONObje
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final JSONArray value) {
+ public static void put(@NonNull final String key, @Nullable final JSONArray value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -189,7 +191,7 @@ public static void put(@NonNull final String key, final JSONArray value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final JSONArray value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final JSONArray value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -210,7 +212,7 @@ public static JSONArray getJSONArray(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the JSONArray if cache exists or defaultValue otherwise
*/
- public static JSONArray getJSONArray(@NonNull final String key, final JSONArray defaultValue) {
+ public static JSONArray getJSONArray(@NonNull final String key, @Nullable final JSONArray defaultValue) {
return getJSONArray(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -225,7 +227,7 @@ public static JSONArray getJSONArray(@NonNull final String key, final JSONArray
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final Bitmap value) {
+ public static void put(@NonNull final String key, @Nullable final Bitmap value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -236,7 +238,7 @@ public static void put(@NonNull final String key, final Bitmap value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final Bitmap value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final Bitmap value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -257,7 +259,7 @@ public static Bitmap getBitmap(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the bitmap if cache exists or defaultValue otherwise
*/
- public static Bitmap getBitmap(@NonNull final String key, final Bitmap defaultValue) {
+ public static Bitmap getBitmap(@NonNull final String key, @Nullable final Bitmap defaultValue) {
return getBitmap(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -271,7 +273,7 @@ public static Bitmap getBitmap(@NonNull final String key, final Bitmap defaultVa
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final Drawable value) {
+ public static void put(@NonNull final String key, @Nullable final Drawable value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -282,7 +284,7 @@ public static void put(@NonNull final String key, final Drawable value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final Drawable value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final Drawable value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -303,7 +305,7 @@ public static Drawable getDrawable(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the drawable if cache exists or defaultValue otherwise
*/
- public static Drawable getDrawable(@NonNull final String key, final Drawable defaultValue) {
+ public static Drawable getDrawable(@NonNull final String key, final @Nullable Drawable defaultValue) {
return getDrawable(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -317,7 +319,7 @@ public static Drawable getDrawable(@NonNull final String key, final Drawable def
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final Parcelable value) {
+ public static void put(@NonNull final String key, @Nullable final Parcelable value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -328,7 +330,7 @@ public static void put(@NonNull final String key, final Parcelable value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final Parcelable value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final Parcelable value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -356,7 +358,7 @@ public static T getParcelable(@NonNull final String key,
*/
public static T getParcelable(@NonNull final String key,
@NonNull final Parcelable.Creator creator,
- final T defaultValue) {
+ @Nullable final T defaultValue) {
return getParcelable(key, creator, defaultValue, getDefaultCacheDiskUtils());
}
@@ -370,7 +372,7 @@ public static T getParcelable(@NonNull final String key,
* @param key The key of cache.
* @param value The value of cache.
*/
- public static void put(@NonNull final String key, final Serializable value) {
+ public static void put(@NonNull final String key, @Nullable final Serializable value) {
put(key, value, getDefaultCacheDiskUtils());
}
@@ -381,7 +383,7 @@ public static void put(@NonNull final String key, final Serializable value) {
* @param value The value of cache.
* @param saveTime The save time of cache, in seconds.
*/
- public static void put(@NonNull final String key, final Serializable value, final int saveTime) {
+ public static void put(@NonNull final String key, @Nullable final Serializable value, final int saveTime) {
put(key, value, saveTime, getDefaultCacheDiskUtils());
}
@@ -402,7 +404,7 @@ public static Object getSerializable(@NonNull final String key) {
* @param defaultValue The default value if the cache doesn't exist.
* @return the bitmap if cache exists or defaultValue otherwise
*/
- public static Object getSerializable(@NonNull final String key, final Object defaultValue) {
+ public static Object getSerializable(@NonNull final String key, @Nullable final Object defaultValue) {
return getSerializable(key, defaultValue, getDefaultCacheDiskUtils());
}
@@ -455,7 +457,7 @@ public static boolean clear() {
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final byte[] value,
+ @Nullable final byte[] value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -469,7 +471,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final byte[] value,
+ @Nullable final byte[] value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -495,7 +497,7 @@ public static byte[] getBytes(@NonNull final String key, @NonNull final CacheDis
* @return the bytes if cache exists or defaultValue otherwise
*/
public static byte[] getBytes(@NonNull final String key,
- final byte[] defaultValue,
+ @Nullable final byte[] defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getBytes(key, defaultValue);
}
@@ -512,7 +514,7 @@ public static byte[] getBytes(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final String value,
+ @Nullable final String value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -526,7 +528,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final String value,
+ @Nullable final String value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -552,7 +554,7 @@ public static String getString(@NonNull final String key, @NonNull final CacheDi
* @return the string value if cache exists or defaultValue otherwise
*/
public static String getString(@NonNull final String key,
- final String defaultValue,
+ @Nullable final String defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getString(key, defaultValue);
}
@@ -569,7 +571,7 @@ public static String getString(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final JSONObject value,
+ @Nullable final JSONObject value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -583,7 +585,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final JSONObject value,
+ @Nullable final JSONObject value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -609,7 +611,7 @@ public static JSONObject getJSONObject(@NonNull final String key, @NonNull final
* @return the JSONObject if cache exists or defaultValue otherwise
*/
public static JSONObject getJSONObject(@NonNull final String key,
- final JSONObject defaultValue,
+ @Nullable final JSONObject defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getJSONObject(key, defaultValue);
}
@@ -627,7 +629,7 @@ public static JSONObject getJSONObject(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final JSONArray value,
+ @Nullable final JSONArray value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -641,7 +643,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final JSONArray value,
+ @Nullable final JSONArray value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -667,7 +669,7 @@ public static JSONArray getJSONArray(@NonNull final String key, @NonNull final C
* @return the JSONArray if cache exists or defaultValue otherwise
*/
public static JSONArray getJSONArray(@NonNull final String key,
- final JSONArray defaultValue,
+ @Nullable final JSONArray defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getJSONArray(key, defaultValue);
}
@@ -685,7 +687,7 @@ public static JSONArray getJSONArray(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Bitmap value,
+ @Nullable final Bitmap value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -699,7 +701,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Bitmap value,
+ @Nullable final Bitmap value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -725,7 +727,7 @@ public static Bitmap getBitmap(@NonNull final String key, @NonNull final CacheDi
* @return the bitmap if cache exists or defaultValue otherwise
*/
public static Bitmap getBitmap(@NonNull final String key,
- final Bitmap defaultValue,
+ @Nullable final Bitmap defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getBitmap(key, defaultValue);
}
@@ -742,7 +744,7 @@ public static Bitmap getBitmap(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Drawable value,
+ @Nullable final Drawable value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -756,7 +758,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Drawable value,
+ @Nullable final Drawable value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -782,7 +784,7 @@ public static Drawable getDrawable(@NonNull final String key, @NonNull final Cac
* @return the drawable if cache exists or defaultValue otherwise
*/
public static Drawable getDrawable(@NonNull final String key,
- final Drawable defaultValue,
+ @Nullable final Drawable defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getDrawable(key, defaultValue);
}
@@ -799,7 +801,7 @@ public static Drawable getDrawable(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Parcelable value,
+ @Nullable final Parcelable value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -813,7 +815,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Parcelable value,
+ @Nullable final Parcelable value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -846,7 +848,7 @@ public static T getParcelable(@NonNull final String key,
*/
public static T getParcelable(@NonNull final String key,
@NonNull final Parcelable.Creator creator,
- final T defaultValue,
+ @Nullable final T defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getParcelable(key, creator, defaultValue);
}
@@ -863,7 +865,7 @@ public static T getParcelable(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Serializable value,
+ @Nullable final Serializable value,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value);
}
@@ -877,7 +879,7 @@ public static void put(@NonNull final String key,
* @param cacheDiskUtils The instance of {@link CacheDiskUtils}.
*/
public static void put(@NonNull final String key,
- final Serializable value,
+ @Nullable final Serializable value,
final int saveTime,
@NonNull final CacheDiskUtils cacheDiskUtils) {
cacheDiskUtils.put(key, value, saveTime);
@@ -903,7 +905,7 @@ public static Object getSerializable(@NonNull final String key, @NonNull final C
* @return the bitmap if cache exists or defaultValue otherwise
*/
public static Object getSerializable(@NonNull final String key,
- final Object defaultValue,
+ @Nullable final Object defaultValue,
@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.getSerializable(key, defaultValue);
}
@@ -949,6 +951,7 @@ public static boolean clear(@NonNull final CacheDiskUtils cacheDiskUtils) {
return cacheDiskUtils.clear();
}
+ @NonNull
private static CacheDiskUtils getDefaultCacheDiskUtils() {
return sDefaultCacheDiskUtils != null ? sDefaultCacheDiskUtils : CacheDiskUtils.getInstance();
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java
index 1c316c6a32..773e550a16 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java
@@ -3,9 +3,10 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.support.annotation.NonNull;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.blankj.utilcode.constant.CacheConstants;
import org.json.JSONArray;
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java
index 2ef267bd01..6dbb9f7475 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java
@@ -3,13 +3,14 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.support.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.Serializable;
+import androidx.annotation.NonNull;
+
/**
*
* author: Blankj
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java
index c800423c78..c39c9ef9d4 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java
@@ -3,7 +3,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
import com.blankj.utilcode.constant.CacheConstants;
@@ -113,7 +113,12 @@ public byte[] getBytes(@NonNull final String key) {
public byte[] getBytes(@NonNull final String key, final byte[] defaultValue) {
byte[] obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getBytes(key, defaultValue);
+ byte[] bytes = mCacheDiskUtils.getBytes(key);
+ if (bytes != null) {
+ mCacheMemoryUtils.put(key, bytes);
+ return bytes;
+ }
+ return defaultValue;
}
///////////////////////////////////////////////////////////////////////////
@@ -162,7 +167,12 @@ public String getString(@NonNull final String key) {
public String getString(@NonNull final String key, final String defaultValue) {
String obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getString(key, defaultValue);
+ String string = mCacheDiskUtils.getString(key);
+ if (string != null) {
+ mCacheMemoryUtils.put(key, string);
+ return string;
+ }
+ return defaultValue;
}
///////////////////////////////////////////////////////////////////////////
@@ -213,7 +223,12 @@ public JSONObject getJSONObject(@NonNull final String key) {
public JSONObject getJSONObject(@NonNull final String key, final JSONObject defaultValue) {
JSONObject obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getJSONObject(key, defaultValue);
+ JSONObject jsonObject = mCacheDiskUtils.getJSONObject(key);
+ if (jsonObject != null) {
+ mCacheMemoryUtils.put(key, jsonObject);
+ return jsonObject;
+ }
+ return defaultValue;
}
@@ -263,7 +278,12 @@ public JSONArray getJSONArray(@NonNull final String key) {
public JSONArray getJSONArray(@NonNull final String key, final JSONArray defaultValue) {
JSONArray obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getJSONArray(key, defaultValue);
+ JSONArray jsonArray = mCacheDiskUtils.getJSONArray(key);
+ if (jsonArray != null) {
+ mCacheMemoryUtils.put(key, jsonArray);
+ return jsonArray;
+ }
+ return defaultValue;
}
///////////////////////////////////////////////////////////////////////////
@@ -312,7 +332,12 @@ public Bitmap getBitmap(@NonNull final String key) {
public Bitmap getBitmap(@NonNull final String key, final Bitmap defaultValue) {
Bitmap obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getBitmap(key, defaultValue);
+ Bitmap bitmap = mCacheDiskUtils.getBitmap(key);
+ if (bitmap != null) {
+ mCacheMemoryUtils.put(key, bitmap);
+ return bitmap;
+ }
+ return defaultValue;
}
///////////////////////////////////////////////////////////////////////////
@@ -361,7 +386,12 @@ public Drawable getDrawable(@NonNull final String key) {
public Drawable getDrawable(@NonNull final String key, final Drawable defaultValue) {
Drawable obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getDrawable(key, defaultValue);
+ Drawable drawable = mCacheDiskUtils.getDrawable(key);
+ if (drawable != null) {
+ mCacheMemoryUtils.put(key, drawable);
+ return drawable;
+ }
+ return defaultValue;
}
///////////////////////////////////////////////////////////////////////////
@@ -417,7 +447,12 @@ public T getParcelable(@NonNull final String key,
final T defaultValue) {
T value = mCacheMemoryUtils.get(key);
if (value != null) return value;
- return mCacheDiskUtils.getParcelable(key, creator, defaultValue);
+ T val = mCacheDiskUtils.getParcelable(key, creator);
+ if (val != null) {
+ mCacheMemoryUtils.put(key, val);
+ return val;
+ }
+ return defaultValue;
}
///////////////////////////////////////////////////////////////////////////
@@ -466,7 +501,12 @@ public Object getSerializable(@NonNull final String key) {
public Object getSerializable(@NonNull final String key, final Object defaultValue) {
Object obj = mCacheMemoryUtils.get(key);
if (obj != null) return obj;
- return mCacheDiskUtils.getSerializable(key, defaultValue);
+ Object serializable = mCacheDiskUtils.getSerializable(key);
+ if (serializable != null) {
+ mCacheMemoryUtils.put(key, serializable);
+ return serializable;
+ }
+ return defaultValue;
}
/**
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java
index 56abfba117..aedcfa7fc4 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java
@@ -1,6 +1,6 @@
package com.blankj.utilcode.util;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
/**
*
+ *
+ * @param bytes The bytes.
+ * @param isUpperCase True to use upper case, false otherwise.
+ * @return hex string
+ */
+ public static String bytes2HexString(final byte[] bytes, boolean isUpperCase) {
if (bytes == null) return "";
+ char[] hexDigits = isUpperCase ? HEX_DIGITS_UPPER : HEX_DIGITS_LOWER;
int len = bytes.length;
if (len <= 0) return "";
char[] ret = new char[len << 1];
@@ -531,7 +566,7 @@ public static ByteArrayOutputStream input2OutputStream(final InputStream is) {
/**
* Output stream to input stream.
*/
- public ByteArrayInputStream output2InputStream(final OutputStream out) {
+ public static ByteArrayInputStream output2InputStream(final OutputStream out) {
if (out == null) return null;
return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray());
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java
index dd049e7e77..598f7fdbac 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java
@@ -1,13 +1,11 @@
package com.blankj.utilcode.util;
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.support.annotation.NonNull;
-
+import androidx.annotation.NonNull;
import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.Map;
/**
*
@@ -21,7 +19,8 @@ public final class CrashUtils {
private static final String FILE_SEP = System.getProperty("file.separator");
- private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
+ private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER =
+ Thread.getDefaultUncaughtExceptionHandler();
private CrashUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
@@ -30,7 +29,6 @@ private CrashUtils() {
/**
* Initialization.
*/
- @SuppressLint("MissingPermission")
public static void init() {
init("");
}
@@ -65,7 +63,7 @@ public static void init(final OnCrashListener onCrashListener) {
/**
* Initialization
*
- * @param crashDir The directory of saving crash information.
+ * @param crashDir The directory of saving crash information.
* @param onCrashListener The crash listener.
*/
public static void init(@NonNull final File crashDir, final OnCrashListener onCrashListener) {
@@ -75,22 +73,23 @@ public static void init(@NonNull final File crashDir, final OnCrashListener onCr
/**
* Initialization
*
- * @param crashDirPath The directory's path of saving crash information.
+ * @param crashDirPath The directory's path of saving crash information.
* @param onCrashListener The crash listener.
*/
public static void init(final String crashDirPath, final OnCrashListener onCrashListener) {
String dirPath;
if (UtilsBridge.isSpace(crashDirPath)) {
if (UtilsBridge.isSDCardEnableByEnvironment()
- && Utils.getApp().getExternalFilesDir(null) != null)
+ && Utils.getApp().getExternalFilesDir(null) != null) {
dirPath = Utils.getApp().getExternalFilesDir(null) + FILE_SEP + "crash" + FILE_SEP;
- else {
+ } else {
dirPath = Utils.getApp().getFilesDir() + FILE_SEP + "crash" + FILE_SEP;
}
} else {
dirPath = crashDirPath.endsWith(FILE_SEP) ? crashDirPath : crashDirPath + FILE_SEP;
}
- Thread.setDefaultUncaughtExceptionHandler(getUncaughtExceptionHandler(dirPath, onCrashListener));
+ Thread.setDefaultUncaughtExceptionHandler(
+ getUncaughtExceptionHandler(dirPath, onCrashListener));
}
private static UncaughtExceptionHandler getUncaughtExceptionHandler(final String dirPath,
@@ -99,28 +98,16 @@ private static UncaughtExceptionHandler getUncaughtExceptionHandler(final String
@Override
public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable e) {
final String time = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss").format(new Date());
- final StringBuilder sb = new StringBuilder();
- final String head = "************* Log Head ****************" +
- "\nTime Of Crash : " + time +
- "\nDevice Manufacturer: " + Build.MANUFACTURER +
- "\nDevice Model : " + Build.MODEL +
- "\nAndroid Version : " + Build.VERSION.RELEASE +
- "\nAndroid SDK : " + Build.VERSION.SDK_INT +
- "\nApp VersionName : " + UtilsBridge.getAppVersionName() +
- "\nApp VersionCode : " + UtilsBridge.getAppVersionCode() +
- "\n************* Log Head ****************\n\n";
- sb.append(head).append(UtilsBridge.getFullStackTrace(e));
- final String crashInfo = sb.toString();
+ CrashInfo info = new CrashInfo(time, e);
final String crashFile = dirPath + time + ".txt";
- UtilsBridge.writeFileFromString(crashFile, crashInfo, true);
-
- if (onCrashListener != null) {
- onCrashListener.onCrash(crashInfo, e);
- }
+ UtilsBridge.writeFileFromString(crashFile, info.toString(), true);
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, e);
}
+ if (onCrashListener != null) {
+ onCrashListener.onCrash(info);
+ }
}
};
}
@@ -130,6 +117,34 @@ public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable
///////////////////////////////////////////////////////////////////////////
public interface OnCrashListener {
- void onCrash(String crashInfo, Throwable e);
+ void onCrash(CrashInfo crashInfo);
+ }
+
+ public static final class CrashInfo {
+ private UtilsBridge.FileHead mFileHeadProvider;
+ private Throwable mThrowable;
+
+ private CrashInfo(String time, Throwable throwable) {
+ mThrowable = throwable;
+ mFileHeadProvider = new UtilsBridge.FileHead("Crash");
+ mFileHeadProvider.addFirst("Time Of Crash", time);
+ }
+
+ public final void addExtraHead(Map extraHead) {
+ mFileHeadProvider.append(extraHead);
+ }
+
+ public final void addExtraHead(String key, String value) {
+ mFileHeadProvider.append(key, value);
+ }
+
+ public final Throwable getThrowable() {
+ return mThrowable;
+ }
+
+ @Override
+ public String toString() {
+ return mFileHeadProvider.toString() + UtilsBridge.getFullStackTrace(mThrowable);
+ }
}
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java
new file mode 100644
index 0000000000..afc49b9c6f
--- /dev/null
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java
@@ -0,0 +1,86 @@
+package com.blankj.utilcode.util;
+
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.view.View;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import androidx.annotation.NonNull;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 2020/09/01
+ * desc : utils about debouncing
+ *
+ */
+public class DebouncingUtils {
+
+ private static final int CACHE_SIZE = 64;
+ private static final Map KEY_MILLIS_MAP = new ConcurrentHashMap<>(CACHE_SIZE);
+ private static final long DEBOUNCING_DEFAULT_VALUE = 1000;
+
+ private DebouncingUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * Return whether the view is not in a jitter state.
+ *
+ * @param view The view.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isValid(@NonNull final View view) {
+ return isValid(view, DEBOUNCING_DEFAULT_VALUE);
+ }
+
+ /**
+ * Return whether the view is not in a jitter state.
+ *
+ * @param view The view.
+ * @param duration The duration.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isValid(@NonNull final View view, final long duration) {
+ return isValid(String.valueOf(view.hashCode()), duration);
+ }
+
+ /**
+ * Return whether the key is not in a jitter state.
+ *
+ * @param key The key.
+ * @param duration The duration.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isValid(@NonNull String key, final long duration) {
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("The key is null.");
+ }
+ if (duration < 0) {
+ throw new IllegalArgumentException("The duration is less than 0.");
+ }
+ long curTime = SystemClock.elapsedRealtime();
+ clearIfNecessary(curTime);
+ Long validTime = KEY_MILLIS_MAP.get(key);
+ if (validTime == null || curTime >= validTime) {
+ KEY_MILLIS_MAP.put(key, curTime + duration);
+ return true;
+ }
+ return false;
+ }
+
+ private static void clearIfNecessary(long curTime) {
+ if (KEY_MILLIS_MAP.size() < CACHE_SIZE) return;
+ for (Iterator> it = KEY_MILLIS_MAP.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = it.next();
+ Long validTime = entry.getValue();
+ if (curTime >= validTime) {
+ it.remove();
+ }
+ }
+ }
+}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java
index 266734aa03..9cd01108e1 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java
@@ -10,18 +10,22 @@
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
-import android.support.annotation.RequiresApi;
-import android.support.annotation.RequiresPermission;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.UUID;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RequiresPermission;
+
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.CHANGE_WIFI_STATE;
import static android.Manifest.permission.INTERNET;
@@ -113,10 +117,10 @@ public static String getAndroidID() {
*
* @return the MAC address
*/
- @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET, CHANGE_WIFI_STATE})
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE})
public static String getMacAddress() {
String macAddress = getMacAddress((String[]) null);
- if (!macAddress.equals("") || getWifiEnabled()) return macAddress;
+ if (!TextUtils.isEmpty(macAddress) || getWifiEnabled()) return macAddress;
setWifiEnabled(true);
setWifiEnabled(false);
return getMacAddress((String[]) null);
@@ -151,7 +155,7 @@ private static void setWifiEnabled(final boolean enabled) {
*
* @return the MAC address
*/
- @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET})
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE})
public static String getMacAddress(final String... excepts) {
String macAddress = getMacAddressByNetworkInterface();
if (isAddressNotInExcepts(macAddress, excepts)) {
@@ -173,25 +177,37 @@ public static String getMacAddress(final String... excepts) {
}
private static boolean isAddressNotInExcepts(final String address, final String... excepts) {
+ if (TextUtils.isEmpty(address)) {
+ return false;
+ }
+ if ("02:00:00:00:00:00".equals(address)) {
+ return false;
+ }
if (excepts == null || excepts.length == 0) {
- return !"02:00:00:00:00:00".equals(address);
+ return true;
}
for (String filter : excepts) {
- if (address.equals(filter)) {
+ if (filter != null && filter.equals(address)) {
return false;
}
}
return true;
}
- @SuppressLint({"MissingPermission", "HardwareIds"})
+ @RequiresPermission(ACCESS_WIFI_STATE)
private static String getMacAddressByWifiInfo() {
try {
final WifiManager wifi = (WifiManager) Utils.getApp()
.getApplicationContext().getSystemService(WIFI_SERVICE);
if (wifi != null) {
final WifiInfo info = wifi.getConnectionInfo();
- if (info != null) return info.getMacAddress();
+ if (info != null) {
+ @SuppressLint("HardwareIds")
+ String macAddress = info.getMacAddress();
+ if (!TextUtils.isEmpty(macAddress)) {
+ return macAddress;
+ }
+ }
}
} catch (Exception e) {
e.printStackTrace();
@@ -369,6 +385,7 @@ public static boolean isEmulator() {
intent.setAction(Intent.ACTION_DIAL);
boolean checkDial = intent.resolveActivity(Utils.getApp().getPackageManager()) == null;
if (checkDial) return true;
+ if (isEmulatorByCpu()) return true;
// boolean checkDebuggerConnected = Debug.isDebuggerConnected();
// if (checkDebuggerConnected) return true;
@@ -376,6 +393,55 @@ public static boolean isEmulator() {
return false;
}
+ /**
+ * Returns whether is emulator by check cpu info.
+ * by function of {@link #readCpuInfo}, obtain the device cpu information.
+ * then compare whether it is intel or amd (because intel and amd are generally not mobile phone cpu), to determine whether it is a real mobile phone
+ *
+ * @return {@code true}: yes {@code false}: no
+ */
+ private static boolean isEmulatorByCpu() {
+ String cpuInfo = readCpuInfo();
+ return cpuInfo.contains("intel") || cpuInfo.contains("amd");
+ }
+
+ /**
+ * Return Cpu information
+ *
+ * @return Cpu info
+ */
+ private static String readCpuInfo() {
+ String result = "";
+ try {
+ String[] args = {"/system/bin/cat", "/proc/cpuinfo"};
+ ProcessBuilder cmd = new ProcessBuilder(args);
+ Process process = cmd.start();
+ StringBuilder sb = new StringBuilder();
+ String readLine;
+ BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8"));
+ while ((readLine = responseReader.readLine()) != null) {
+ sb.append(readLine);
+ }
+ responseReader.close();
+ result = sb.toString().toLowerCase();
+ } catch (IOException ignored) {
+ }
+ return result;
+ }
+
+ /**
+ * Whether user has enabled development settings.
+ *
+ * @return whether user has enabled development settings.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
+ public static boolean isDevelopmentSettingsEnabled() {
+ return Settings.Global.getInt(
+ Utils.getApp().getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0
+ ) > 0;
+ }
+
private static final String KEY_UDID = "KEY_UDID";
private volatile static String udid;
@@ -388,7 +454,6 @@ public static boolean isEmulator() {
*
* @return the unique device id
*/
- @SuppressLint({"MissingPermission", "HardwareIds"})
public static String getUniqueDeviceId() {
return getUniqueDeviceId("", true);
}
@@ -402,7 +467,6 @@ public static String getUniqueDeviceId() {
* @param prefix The prefix of the unique device id.
* @return the unique device id
*/
- @SuppressLint({"MissingPermission", "HardwareIds"})
public static String getUniqueDeviceId(String prefix) {
return getUniqueDeviceId(prefix, true);
}
@@ -416,7 +480,6 @@ public static String getUniqueDeviceId(String prefix) {
* @param useCache True to use cache, false otherwise.
* @return the unique device id
*/
- @SuppressLint({"MissingPermission", "HardwareIds"})
public static String getUniqueDeviceId(boolean useCache) {
return getUniqueDeviceId("", useCache);
}
@@ -431,7 +494,6 @@ public static String getUniqueDeviceId(boolean useCache) {
* @param useCache True to use cache, false otherwise.
* @return the unique device id
*/
- @SuppressLint({"MissingPermission", "HardwareIds"})
public static String getUniqueDeviceId(String prefix, boolean useCache) {
if (!useCache) {
return getUniqueDeviceIdReal(prefix);
@@ -462,7 +524,7 @@ private static String getUniqueDeviceIdReal(String prefix) {
return saveUdid(prefix + 9, "");
}
- @SuppressLint({"MissingPermission", "HardwareIds"})
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET, CHANGE_WIFI_STATE})
public static boolean isSameDevice(final String uniqueDeviceId) {
// {prefix}{type}{32id}
if (TextUtils.isEmpty(uniqueDeviceId) && uniqueDeviceId.length() < 33) return false;
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java
index 14d8d0f377..933f8e4953 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java
@@ -1,5 +1,7 @@
package com.blankj.utilcode.util;
+import android.os.Build;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -10,15 +12,11 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
-import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
-import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
@@ -1085,12 +1083,18 @@ private static byte[] rsaTemplate(final byte[] data,
}
try {
Key rsaKey;
+ KeyFactory keyFactory;
+ if (Build.VERSION.SDK_INT < 28) {
+ keyFactory = KeyFactory.getInstance("RSA", "BC");
+ } else {
+ keyFactory = KeyFactory.getInstance("RSA");
+ }
if (isEncrypt) {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
- rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
+ rsaKey = keyFactory.generatePublic(keySpec);
} else {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
- rsaKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
+ rsaKey = keyFactory.generatePrivate(keySpec);
}
if (rsaKey == null) return null;
Cipher cipher = Cipher.getInstance(transformation);
@@ -1123,17 +1127,7 @@ private static byte[] rsaTemplate(final byte[] data,
} else {
return cipher.doFinal(data);
}
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (NoSuchPaddingException e) {
- e.printStackTrace();
- } catch (InvalidKeyException e) {
- e.printStackTrace();
- } catch (BadPaddingException e) {
- e.printStackTrace();
- } catch (IllegalBlockSizeException e) {
- e.printStackTrace();
- } catch (InvalidKeySpecException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
return null;
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java
index 761550d0b6..ca786e0b98 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java
@@ -376,7 +376,11 @@ public static boolean writeFileFromBytesByChannel(final File file,
final byte[] bytes,
final boolean append,
final boolean isForce) {
- if (bytes == null || !UtilsBridge.createOrExistsFile(file)) {
+ if (bytes == null) {
+ Log.e("FileIOUtils", "bytes is null.");
+ return false;
+ }
+ if (!UtilsBridge.createOrExistsFile(file)) {
Log.e("FileIOUtils", "create file <" + file + "> failed.");
return false;
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java
index 217af05fa8..1acca1e050 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java
@@ -430,12 +430,14 @@ private static boolean copyOrMoveDir(final File srcDir,
if (!srcDir.exists() || !srcDir.isDirectory()) return false;
if (!createOrExistsDir(destDir)) return false;
File[] files = srcDir.listFiles();
- for (File file : files) {
- File oneDestFile = new File(destPath + file.getName());
- if (file.isFile()) {
- if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false;
- } else if (file.isDirectory()) {
- if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false;
+ if (files != null && files.length > 0) {
+ for (File file : files) {
+ File oneDestFile = new File(destPath + file.getName());
+ if (file.isFile()) {
+ if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false;
+ } else if (file.isDirectory()) {
+ if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false;
+ }
}
}
return !isMove || deleteDir(srcDir);
@@ -506,7 +508,7 @@ private static boolean deleteDir(final File dir) {
// dir isn't a directory then return false
if (!dir.isDirectory()) return false;
File[] files = dir.listFiles();
- if (files != null && files.length != 0) {
+ if (files != null && files.length > 0) {
for (File file : files) {
if (file.isFile()) {
if (!file.delete()) return false;
@@ -649,7 +651,7 @@ public static List listFilesInDir(final File dir) {
* @return the files in directory
*/
public static List listFilesInDir(final String dirPath, Comparator comparator) {
- return listFilesInDir(getFileByPath(dirPath), false);
+ return listFilesInDir(getFileByPath(dirPath), false, comparator);
}
/**
@@ -846,7 +848,7 @@ private static List listFilesInDirWithFilterInner(final File dir,
List list = new ArrayList<>();
if (!isDir(dir)) return list;
File[] files = dir.listFiles();
- if (files != null && files.length != 0) {
+ if (files != null && files.length > 0) {
for (File file : files) {
if (filter.accept(file)) {
list.add(file);
@@ -1166,10 +1168,10 @@ public static long getLength(final File file) {
* @return the length of directory
*/
private static long getDirLength(final File dir) {
- if (!isDir(dir)) return -1;
+ if (!isDir(dir)) return 0;
long len = 0;
File[] files = dir.listFiles();
- if (files != null && files.length != 0) {
+ if (files != null && files.length > 0) {
for (File file : files) {
if (file.isDirectory()) {
len += getDirLength(file);
@@ -1398,8 +1400,7 @@ public static void notifySystemToScan(final String filePath) {
public static void notifySystemToScan(final File file) {
if (file == null || !file.exists()) return;
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- Uri uri = Uri.fromFile(file);
- intent.setData(uri);
+ intent.setData(Uri.parse("file://" + file.getAbsolutePath()));
Utils.getApp().sendBroadcast(intent);
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java
index 1b864f6ef5..e74996d4e4 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java
@@ -65,7 +65,7 @@ public static void setFlashlightStatus(final boolean isOn) {
parameters.setFlashMode(FLASH_MODE_TORCH);
mCamera.setParameters(parameters);
} catch (IOException e) {
- Log.e("FlashlightUtils", "setFlashlightStatusOn: ", e);
+ e.printStackTrace();
}
}
} else {
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java
index 0916aa3b1d..31bbac1217 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java
@@ -3,23 +3,25 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.AnimRes;
-import android.support.annotation.AnimatorRes;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.IdRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import androidx.annotation.AnimRes;
+import androidx.annotation.AnimatorRes;
+import androidx.annotation.ColorInt;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
/**
*
* author: Blankj
@@ -301,7 +303,7 @@ public static void add(@NonNull final FragmentManager fm,
final boolean isHide,
final boolean isAddStack) {
putArgs(add, new Args(containerId, tag, isHide, isAddStack));
- operateNoAnim(fm, TYPE_ADD_FRAGMENT, null, add);
+ operateNoAnim(TYPE_ADD_FRAGMENT, fm, null, add);
}
/**
@@ -489,7 +491,7 @@ public static void add(@NonNull final FragmentManager fm,
putArgs(adds[i], new Args(containerId, tags[i], showIndex != i, false));
}
}
- operateNoAnim(fm, TYPE_ADD_FRAGMENT, null, adds);
+ operateNoAnim(TYPE_ADD_FRAGMENT, fm, null, adds);
}
/**
@@ -499,7 +501,7 @@ public static void add(@NonNull final FragmentManager fm,
*/
public static void show(@NonNull final Fragment show) {
putArgs(show, false);
- operateNoAnim(show.getFragmentManager(), TYPE_SHOW_FRAGMENT, null, show);
+ operateNoAnim(TYPE_SHOW_FRAGMENT, show.getFragmentManager(), null, show);
}
/**
@@ -512,11 +514,7 @@ public static void show(@NonNull final FragmentManager fm) {
for (Fragment show : fragments) {
putArgs(show, false);
}
- operateNoAnim(fm,
- TYPE_SHOW_FRAGMENT,
- null,
- fragments.toArray(new Fragment[0])
- );
+ operateNoAnim(TYPE_SHOW_FRAGMENT, fm, null, fragments.toArray(new Fragment[0]));
}
/**
@@ -526,7 +524,7 @@ public static void show(@NonNull final FragmentManager fm) {
*/
public static void hide(@NonNull final Fragment hide) {
putArgs(hide, true);
- operateNoAnim(hide.getFragmentManager(), TYPE_HIDE_FRAGMENT, null, hide);
+ operateNoAnim(TYPE_HIDE_FRAGMENT, hide.getFragmentManager(), null, hide);
}
/**
@@ -539,21 +537,28 @@ public static void hide(@NonNull final FragmentManager fm) {
for (Fragment hide : fragments) {
putArgs(hide, true);
}
- operateNoAnim(fm,
- TYPE_HIDE_FRAGMENT,
- null,
- fragments.toArray(new Fragment[0])
- );
+ operateNoAnim(TYPE_HIDE_FRAGMENT, fm, null, fragments.toArray(new Fragment[0]));
+ }
+
+ /**
+ * Show fragment then hide other fragment.
+ *
+ * @param show The fragment will be show.
+ * @param hide The fragment will be hide.
+ */
+ public static void showHide(@NonNull final Fragment show,
+ @NonNull final Fragment hide) {
+ showHide(show, Collections.singletonList(hide));
}
/**
* Show fragment then hide other fragment.
*
* @param showIndex The index of fragment will be shown.
- * @param fragments The fragments will be hide.
+ * @param fragments The fragment will be hide.
*/
- public static void showHide(final int showIndex, @NonNull final List fragments) {
- showHide(fragments.get(showIndex), fragments);
+ public static void showHide(final int showIndex, @NonNull final Fragment... fragments) {
+ showHide(fragments[showIndex], fragments);
}
/**
@@ -562,22 +567,18 @@ public static void showHide(final int showIndex, @NonNull final List f
* @param show The fragment will be show.
* @param hide The fragment will be hide.
*/
- public static void showHide(@NonNull final Fragment show, @NonNull final List hide) {
- for (Fragment fragment : hide) {
- putArgs(fragment, fragment != show);
- }
- operateNoAnim(show.getFragmentManager(), TYPE_SHOW_HIDE_FRAGMENT, show,
- hide.toArray(new Fragment[0]));
+ public static void showHide(@NonNull final Fragment show, @NonNull final Fragment... hide) {
+ showHide(show, Arrays.asList(hide));
}
/**
* Show fragment then hide other fragment.
*
* @param showIndex The index of fragment will be shown.
- * @param fragments The fragment will be hide.
+ * @param fragments The fragments will be hide.
*/
- public static void showHide(final int showIndex, @NonNull final Fragment... fragments) {
- showHide(fragments[showIndex], fragments);
+ public static void showHide(final int showIndex, @NonNull final List fragments) {
+ showHide(fragments.get(showIndex), fragments);
}
/**
@@ -586,13 +587,14 @@ public static void showHide(final int showIndex, @NonNull final Fragment... frag
* @param show The fragment will be show.
* @param hide The fragment will be hide.
*/
- public static void showHide(@NonNull final Fragment show, @NonNull final Fragment... hide) {
+ public static void showHide(@NonNull final Fragment show, @NonNull final List hide) {
for (Fragment fragment : hide) {
putArgs(fragment, fragment != show);
}
- operateNoAnim(show.getFragmentManager(), TYPE_SHOW_HIDE_FRAGMENT, show, hide);
+ operateNoAnim(TYPE_SHOW_HIDE_FRAGMENT, show.getFragmentManager(), show, hide.toArray(new Fragment[0]));
}
+
/**
* Show fragment then hide other fragment.
*
@@ -600,10 +602,47 @@ public static void showHide(@NonNull final Fragment show, @NonNull final Fragmen
* @param hide The fragment will be hide.
*/
public static void showHide(@NonNull final Fragment show,
- @NonNull final Fragment hide) {
- putArgs(show, false);
- putArgs(hide, true);
- operateNoAnim(show.getFragmentManager(), TYPE_SHOW_HIDE_FRAGMENT, show, hide);
+ @NonNull final Fragment hide, @AnimatorRes @AnimRes final int enterAnim,
+ @AnimatorRes @AnimRes final int exitAnim,
+ @AnimatorRes @AnimRes final int popEnterAnim,
+ @AnimatorRes @AnimRes final int popExitAnim) {
+ showHide(show, Collections.singletonList(hide), enterAnim, exitAnim, popEnterAnim, popExitAnim);
+ }
+
+ /**
+ * Show fragment then hide other fragment.
+ *
+ * @param showIndex The index of fragment will be shown.
+ * @param fragments The fragments will be hide.
+ */
+ public static void showHide(final int showIndex, @NonNull final List fragments,
+ @AnimatorRes @AnimRes final int enterAnim,
+ @AnimatorRes @AnimRes final int exitAnim,
+ @AnimatorRes @AnimRes final int popEnterAnim,
+ @AnimatorRes @AnimRes final int popExitAnim) {
+ showHide(fragments.get(showIndex), fragments, enterAnim, exitAnim, popEnterAnim, popExitAnim);
+ }
+
+ /**
+ * Show fragment then hide other fragment.
+ *
+ * @param show The fragment will be show.
+ * @param hide The fragment will be hide.
+ */
+ public static void showHide(@NonNull final Fragment show, @NonNull final List hide,
+ @AnimatorRes @AnimRes final int enterAnim,
+ @AnimatorRes @AnimRes final int exitAnim,
+ @AnimatorRes @AnimRes final int popEnterAnim,
+ @AnimatorRes @AnimRes final int popExitAnim) {
+ for (Fragment fragment : hide) {
+ putArgs(fragment, fragment != show);
+ }
+ FragmentManager fm = show.getFragmentManager();
+ if (fm != null) {
+ FragmentTransaction ft = fm.beginTransaction();
+ addAnim(ft, enterAnim, exitAnim, popEnterAnim, popExitAnim);
+ operate(TYPE_SHOW_HIDE_FRAGMENT, fm, ft, show, hide.toArray(new Fragment[0]));
+ }
}
/**
@@ -1357,7 +1396,7 @@ public static void popAll(@NonNull final FragmentManager fm, final boolean isImm
* @param remove The fragment will be removed.
*/
public static void remove(@NonNull final Fragment remove) {
- operateNoAnim(remove.getFragmentManager(), TYPE_REMOVE_FRAGMENT, null, remove);
+ operateNoAnim(TYPE_REMOVE_FRAGMENT, remove.getFragmentManager(), null, remove);
}
/**
@@ -1367,8 +1406,7 @@ public static void remove(@NonNull final Fragment remove) {
* @param isIncludeSelf True to include the fragment, false otherwise.
*/
public static void removeTo(@NonNull final Fragment removeTo, final boolean isIncludeSelf) {
- operateNoAnim(removeTo.getFragmentManager(), TYPE_REMOVE_TO_FRAGMENT,
- isIncludeSelf ? removeTo : null, removeTo);
+ operateNoAnim(TYPE_REMOVE_TO_FRAGMENT, removeTo.getFragmentManager(), isIncludeSelf ? removeTo : null, removeTo);
}
/**
@@ -1378,11 +1416,7 @@ public static void removeTo(@NonNull final Fragment removeTo, final boolean isIn
*/
public static void removeAll(@NonNull final FragmentManager fm) {
List fragments = getFragments(fm);
- operateNoAnim(fm,
- TYPE_REMOVE_FRAGMENT,
- null,
- fragments.toArray(new Fragment[0])
- );
+ operateNoAnim(TYPE_REMOVE_FRAGMENT, fm, null, fragments.toArray(new Fragment[0]));
}
private static void putArgs(final Fragment fragment, final Args args) {
@@ -1414,8 +1448,7 @@ private static Args getArgs(final Fragment fragment) {
bundle.getBoolean(ARGS_IS_ADD_STACK));
}
- private static void operateNoAnim(@Nullable final FragmentManager fm,
- final int type,
+ private static void operateNoAnim(final int type, @Nullable final FragmentManager fm,
final Fragment src,
Fragment... dest) {
if (fm == null) return;
@@ -1493,6 +1526,7 @@ private static void operate(final int type,
break;
}
ft.commitAllowingStateLoss();
+ fm.executePendingTransactions();
}
private static void addAnim(final FragmentTransaction ft,
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java
index 53ba83a6ee..c3eb0684c6 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java
@@ -1,6 +1,5 @@
package com.blankj.utilcode.util;
-import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.google.gson.Gson;
@@ -9,10 +8,12 @@
import java.io.Reader;
import java.lang.reflect.Type;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import androidx.annotation.NonNull;
/**
@@ -29,7 +30,7 @@ public final class GsonUtils {
private static final String KEY_DELEGATE = "delegateGson";
private static final String KEY_LOG_UTILS = "logUtilsGson";
- private static final Map GSONS = new HashMap<>();
+ private static final Map GSONS = new ConcurrentHashMap<>();
private GsonUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
@@ -85,7 +86,7 @@ public static Gson getGson() {
* @param object The object to serialize.
* @return object serialized into json.
*/
- public static String toJson(@NonNull final Object object) {
+ public static String toJson(final Object object) {
return toJson(getGson(), object);
}
@@ -96,7 +97,7 @@ public static String toJson(@NonNull final Object object) {
* @param typeOfSrc The specific genericized type of src.
* @return object serialized into json.
*/
- public static String toJson(@NonNull final Object src, @NonNull final Type typeOfSrc) {
+ public static String toJson(final Object src, @NonNull final Type typeOfSrc) {
return toJson(getGson(), src, typeOfSrc);
}
@@ -107,7 +108,7 @@ public static String toJson(@NonNull final Object src, @NonNull final Type typeO
* @param object The object to serialize.
* @return object serialized into json.
*/
- public static String toJson(@NonNull final Gson gson, @NonNull final Object object) {
+ public static String toJson(@NonNull final Gson gson, final Object object) {
return gson.toJson(object);
}
@@ -119,7 +120,7 @@ public static String toJson(@NonNull final Gson gson, @NonNull final Object obje
* @param typeOfSrc The specific genericized type of src.
* @return object serialized into json.
*/
- public static String toJson(@NonNull final Gson gson, @NonNull final Object src, @NonNull final Type typeOfSrc) {
+ public static String toJson(@NonNull final Gson gson, final Object src, @NonNull final Type typeOfSrc) {
return gson.toJson(src, typeOfSrc);
}
@@ -130,7 +131,7 @@ public static String toJson(@NonNull final Gson gson, @NonNull final Object src,
* @param type Type json will be converted to.
* @return instance of type
*/
- public static T fromJson(@NonNull final String json, @NonNull final Class type) {
+ public static T fromJson(final String json, @NonNull final Class type) {
return fromJson(getGson(), json, type);
}
@@ -141,7 +142,7 @@ public static T fromJson(@NonNull final String json, @NonNull final Class
* @param type type type json will be converted to.
* @return instance of type
*/
- public static T fromJson(@NonNull final String json, @NonNull final Type type) {
+ public static T fromJson(final String json, @NonNull final Type type) {
return fromJson(getGson(), json, type);
}
@@ -175,7 +176,7 @@ public static T fromJson(@NonNull final Reader reader, @NonNull final Type t
* @param type Type json will be converted to.
* @return instance of type
*/
- public static T fromJson(@NonNull final Gson gson, @NonNull final String json, @NonNull final Class type) {
+ public static T fromJson(@NonNull final Gson gson, final String json, @NonNull final Class type) {
return gson.fromJson(json, type);
}
@@ -187,7 +188,7 @@ public static T fromJson(@NonNull final Gson gson, @NonNull final String jso
* @param type type type json will be converted to.
* @return instance of type
*/
- public static T fromJson(@NonNull final Gson gson, @NonNull final String json, @NonNull final Type type) {
+ public static T fromJson(@NonNull final Gson gson, final String json, @NonNull final Type type) {
return gson.fromJson(json, type);
}
@@ -199,7 +200,7 @@ public static T fromJson(@NonNull final Gson gson, @NonNull final String jso
* @param type type type json will be converted to.
* @return instance of type
*/
- public static T fromJson(@NonNull final Gson gson, @NonNull final Reader reader, @NonNull final Class type) {
+ public static T fromJson(@NonNull final Gson gson, final Reader reader, @NonNull final Class type) {
return gson.fromJson(reader, type);
}
@@ -211,7 +212,7 @@ public static T fromJson(@NonNull final Gson gson, @NonNull final Reader rea
* @param type type type json will be converted to.
* @return instance of type
*/
- public static T fromJson(@NonNull final Gson gson, @NonNull final Reader reader, @NonNull final Type type) {
+ public static T fromJson(@NonNull final Gson gson, final Reader reader, @NonNull final Type type) {
return gson.fromJson(reader, type);
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java
index 7a10fb532e..e0d472428f 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java
@@ -1,5 +1,7 @@
package com.blankj.utilcode.util;
+import android.Manifest;
+import android.content.ContentValues;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
@@ -12,6 +14,7 @@
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
@@ -22,18 +25,15 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
+import android.net.Uri;
import android.os.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
-import android.support.annotation.ColorInt;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.FloatRange;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -47,6 +47,15 @@
import java.io.InputStream;
import java.io.OutputStream;
+import androidx.annotation.ColorInt;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.content.ContextCompat;
+
/**
*
* author: Blankj
@@ -79,7 +88,7 @@ public static byte[] bitmap2Bytes(final Bitmap bitmap) {
* @param quality The quality.
* @return bytes
*/
- public static byte[] bitmap2Bytes(final Bitmap bitmap, final CompressFormat format, int quality) {
+ public static byte[] bitmap2Bytes(@Nullable final Bitmap bitmap, @NonNull final CompressFormat format, int quality) {
if (bitmap == null) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(format, quality, baos);
@@ -92,7 +101,7 @@ public static byte[] bitmap2Bytes(final Bitmap bitmap, final CompressFormat form
* @param bytes The bytes.
* @return bitmap
*/
- public static Bitmap bytes2Bitmap(final byte[] bytes) {
+ public static Bitmap bytes2Bitmap(@Nullable final byte[] bytes) {
return (bytes == null || bytes.length == 0)
? null
: BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
@@ -104,7 +113,8 @@ public static Bitmap bytes2Bitmap(final byte[] bytes) {
* @param drawable The drawable.
* @return bitmap
*/
- public static Bitmap drawable2Bitmap(final Drawable drawable) {
+ public static Bitmap drawable2Bitmap(@Nullable final Drawable drawable) {
+ if (drawable == null) return null;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if (bitmapDrawable.getBitmap() != null) {
@@ -136,7 +146,7 @@ public static Bitmap drawable2Bitmap(final Drawable drawable) {
* @param bitmap The bitmap.
* @return drawable
*/
- public static Drawable bitmap2Drawable(final Bitmap bitmap) {
+ public static Drawable bitmap2Drawable(@Nullable final Bitmap bitmap) {
return bitmap == null ? null : new BitmapDrawable(Utils.getApp().getResources(), bitmap);
}
@@ -146,7 +156,7 @@ public static Drawable bitmap2Drawable(final Bitmap bitmap) {
* @param drawable The drawable.
* @return bytes
*/
- public static byte[] drawable2Bytes(final Drawable drawable) {
+ public static byte[] drawable2Bytes(@Nullable final Drawable drawable) {
return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable));
}
@@ -183,18 +193,24 @@ public static Bitmap view2Bitmap(final View view) {
boolean willNotCacheDrawing = view.willNotCacheDrawing();
view.setDrawingCacheEnabled(true);
view.setWillNotCacheDrawing(false);
- final Bitmap drawingCache = view.getDrawingCache();
+ Bitmap drawingCache = view.getDrawingCache();
Bitmap bitmap;
- if (null == drawingCache) {
+ if (null == drawingCache || drawingCache.isRecycled()) {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.buildDrawingCache();
- bitmap = Bitmap.createBitmap(view.getDrawingCache());
+ drawingCache = view.getDrawingCache();
+ if (null == drawingCache || drawingCache.isRecycled()) {
+ bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ } else {
+ bitmap = Bitmap.createBitmap(drawingCache);
+ }
} else {
bitmap = Bitmap.createBitmap(drawingCache);
}
- view.destroyDrawingCache();
view.setWillNotCacheDrawing(willNotCacheDrawing);
view.setDrawingCacheEnabled(drawingCacheEnabled);
return bitmap;
@@ -279,8 +295,12 @@ public static Bitmap getBitmap(final InputStream is) {
*/
public static Bitmap getBitmap(final InputStream is, final int maxWidth, final int maxHeight) {
if (is == null) return null;
- byte[] bytes = UtilsBridge.inputStream2Bytes(is);
- return getBitmap(bytes, 0, maxWidth, maxHeight);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeStream(is, null, options);
}
/**
@@ -325,6 +345,9 @@ public static Bitmap getBitmap(final byte[] data,
*/
public static Bitmap getBitmap(@DrawableRes final int resId) {
Drawable drawable = ContextCompat.getDrawable(Utils.getApp(), resId);
+ if (drawable == null) {
+ return null;
+ }
Canvas canvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
@@ -775,11 +798,27 @@ public static Bitmap toRoundCorner(final Bitmap src,
*/
public static Bitmap toRoundCorner(final Bitmap src,
final float radius,
- @IntRange(from = 0) int borderSize,
+ @FloatRange(from = 0) float borderSize,
@ColorInt int borderColor) {
return toRoundCorner(src, radius, borderSize, borderColor, false);
}
+ /**
+ * Return the round corner bitmap.
+ *
+ * @param src The source of bitmap.
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @param borderSize The size of border.
+ * @param borderColor The color of border.
+ * @return the round corner bitmap
+ */
+ public static Bitmap toRoundCorner(final Bitmap src,
+ final float[] radii,
+ @FloatRange(from = 0) float borderSize,
+ @ColorInt int borderColor) {
+ return toRoundCorner(src, radii, borderSize, borderColor, false);
+ }
+
/**
* Return the round corner bitmap.
*
@@ -792,7 +831,26 @@ public static Bitmap toRoundCorner(final Bitmap src,
*/
public static Bitmap toRoundCorner(final Bitmap src,
final float radius,
- @IntRange(from = 0) int borderSize,
+ @FloatRange(from = 0) float borderSize,
+ @ColorInt int borderColor,
+ final boolean recycle) {
+ float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius};
+ return toRoundCorner(src, radii, borderSize, borderColor, recycle);
+ }
+
+ /**
+ * Return the round corner bitmap.
+ *
+ * @param src The source of bitmap.
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @param borderSize The size of border.
+ * @param borderColor The color of border.
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the round corner bitmap
+ */
+ public static Bitmap toRoundCorner(final Bitmap src,
+ final float[] radii,
+ @FloatRange(from = 0) float borderSize,
@ColorInt int borderColor,
final boolean recycle) {
if (isEmptyBitmap(src)) return null;
@@ -806,14 +864,16 @@ public static Bitmap toRoundCorner(final Bitmap src,
RectF rectF = new RectF(0, 0, width, height);
float halfBorderSize = borderSize / 2f;
rectF.inset(halfBorderSize, halfBorderSize);
- canvas.drawRoundRect(rectF, radius, radius, paint);
+ Path path = new Path();
+ path.addRoundRect(rectF, radii, Path.Direction.CW);
+ canvas.drawPath(path, paint);
if (borderSize > 0) {
paint.setShader(null);
paint.setColor(borderColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(borderSize);
paint.setStrokeCap(Paint.Cap.ROUND);
- canvas.drawRoundRect(rectF, radius, radius, paint);
+ canvas.drawPath(path, paint);
}
if (recycle && !src.isRecycled() && ret != src) src.recycle();
return ret;
@@ -829,12 +889,46 @@ public static Bitmap toRoundCorner(final Bitmap src,
* @return the round corner bitmap with border
*/
public static Bitmap addCornerBorder(final Bitmap src,
- @IntRange(from = 1) final int borderSize,
+ @FloatRange(from = 1) final float borderSize,
@ColorInt final int color,
@FloatRange(from = 0) final float cornerRadius) {
return addBorder(src, borderSize, color, false, cornerRadius, false);
}
+ /**
+ * Return the round corner bitmap with border.
+ *
+ * @param src The source of bitmap.
+ * @param borderSize The size of border.
+ * @param color The color of border.
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @return the round corner bitmap with border
+ */
+ public static Bitmap addCornerBorder(final Bitmap src,
+ @FloatRange(from = 1) final float borderSize,
+ @ColorInt final int color,
+ final float[] radii) {
+ return addBorder(src, borderSize, color, false, radii, false);
+ }
+
+ /**
+ * Return the round corner bitmap with border.
+ *
+ * @param src The source of bitmap.
+ * @param borderSize The size of border.
+ * @param color The color of border.
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the round corner bitmap with border
+ */
+ public static Bitmap addCornerBorder(final Bitmap src,
+ @FloatRange(from = 1) final float borderSize,
+ @ColorInt final int color,
+ final float[] radii,
+ final boolean recycle) {
+ return addBorder(src, borderSize, color, false, radii, recycle);
+ }
+
/**
* Return the round corner bitmap with border.
*
@@ -846,7 +940,7 @@ public static Bitmap addCornerBorder(final Bitmap src,
* @return the round corner bitmap with border
*/
public static Bitmap addCornerBorder(final Bitmap src,
- @IntRange(from = 1) final int borderSize,
+ @FloatRange(from = 1) final float borderSize,
@ColorInt final int color,
@FloatRange(from = 0) final float cornerRadius,
final boolean recycle) {
@@ -862,7 +956,7 @@ public static Bitmap addCornerBorder(final Bitmap src,
* @return the round bitmap with border
*/
public static Bitmap addCircleBorder(final Bitmap src,
- @IntRange(from = 1) final int borderSize,
+ @FloatRange(from = 1) final float borderSize,
@ColorInt final int color) {
return addBorder(src, borderSize, color, true, 0, false);
}
@@ -877,7 +971,7 @@ public static Bitmap addCircleBorder(final Bitmap src,
* @return the round bitmap with border
*/
public static Bitmap addCircleBorder(final Bitmap src,
- @IntRange(from = 1) final int borderSize,
+ @FloatRange(from = 1) final float borderSize,
@ColorInt final int color,
final boolean recycle) {
return addBorder(src, borderSize, color, true, 0, recycle);
@@ -895,11 +989,33 @@ public static Bitmap addCircleBorder(final Bitmap src,
* @return the bitmap with border
*/
private static Bitmap addBorder(final Bitmap src,
- @IntRange(from = 1) final int borderSize,
+ @FloatRange(from = 1) final float borderSize,
@ColorInt final int color,
final boolean isCircle,
final float cornerRadius,
final boolean recycle) {
+ float[] radii = {cornerRadius, cornerRadius, cornerRadius, cornerRadius,
+ cornerRadius, cornerRadius, cornerRadius, cornerRadius};
+ return addBorder(src, borderSize, color, isCircle, radii, recycle);
+ }
+
+ /**
+ * Return the bitmap with border.
+ *
+ * @param src The source of bitmap.
+ * @param borderSize The size of border.
+ * @param color The color of border.
+ * @param isCircle True to draw circle, false to draw corner.
+ * @param radii Array of 8 values, 4 pairs of [X,Y] radii
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the bitmap with border
+ */
+ private static Bitmap addBorder(final Bitmap src,
+ @FloatRange(from = 1) final float borderSize,
+ @ColorInt final int color,
+ final boolean isCircle,
+ final float[] radii,
+ final boolean recycle) {
if (isEmptyBitmap(src)) return null;
Bitmap ret = recycle ? src : src.copy(src.getConfig(), true);
int width = ret.getWidth();
@@ -913,10 +1029,12 @@ private static Bitmap addBorder(final Bitmap src,
float radius = Math.min(width, height) / 2f - borderSize / 2f;
canvas.drawCircle(width / 2f, height / 2f, radius, paint);
} else {
- int halfBorderSize = borderSize >> 1;
- RectF rectF = new RectF(halfBorderSize, halfBorderSize,
- width - halfBorderSize, height - halfBorderSize);
- canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
+ RectF rectF = new RectF(0, 0, width, height);
+ float halfBorderSize = borderSize / 2f;
+ rectF.inset(halfBorderSize, halfBorderSize);
+ Path path = new Path();
+ path.addRoundRect(rectF, radii, Path.Direction.CW);
+ canvas.drawPath(path, paint);
}
return ret;
}
@@ -1490,7 +1608,7 @@ public static Bitmap stackBlur(final Bitmap src, int radius, final boolean recyc
public static boolean save(final Bitmap src,
final String filePath,
final CompressFormat format) {
- return save(src, UtilsBridge.getFileByPath(filePath), format, false);
+ return save(src, filePath, format, 100, false);
}
/**
@@ -1502,7 +1620,73 @@ public static boolean save(final Bitmap src,
* @return {@code true}: success {@code false}: fail
*/
public static boolean save(final Bitmap src, final File file, final CompressFormat format) {
- return save(src, file, format, false);
+ return save(src, file, format, 100, false);
+ }
+
+ /**
+ * Save the bitmap.
+ *
+ * @param src The source of bitmap.
+ * @param filePath The path of file.
+ * @param format The format of the image.
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return {@code true}: success {@code false}: fail
+ */
+ public static boolean save(final Bitmap src,
+ final String filePath,
+ final CompressFormat format,
+ final boolean recycle) {
+ return save(src, filePath, format, 100, recycle);
+ }
+
+ /**
+ * Save the bitmap.
+ *
+ * @param src The source of bitmap.
+ * @param file The file.
+ * @param format The format of the image.
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return {@code true}: success {@code false}: fail
+ */
+ public static boolean save(final Bitmap src,
+ final File file,
+ final CompressFormat format,
+ final boolean recycle) {
+ return save(src, file, format, 100, recycle);
+ }
+
+ /**
+ * Save the bitmap.
+ *
+ * @param src The source of bitmap.
+ * @param filePath The path of file.
+ * @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
+ * @return {@code true}: success {@code false}: fail
+ */
+ public static boolean save(final Bitmap src,
+ final String filePath,
+ final CompressFormat format,
+ final int quality) {
+ return save(src, UtilsBridge.getFileByPath(filePath), format, quality, false);
+ }
+
+ /**
+ * Save the bitmap.
+ *
+ * @param src The source of bitmap.
+ * @param file The file.
+ * @param format The format of the image.
+ * @return {@code true}: success {@code false}: fail
+ */
+ public static boolean save(final Bitmap src,
+ final File file,
+ final CompressFormat format,
+ final int quality) {
+ return save(src, file, format, quality, false);
}
/**
@@ -1511,14 +1695,19 @@ public static boolean save(final Bitmap src, final File file, final CompressForm
* @param src The source of bitmap.
* @param filePath The path of file.
* @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
* @param recycle True to recycle the source of bitmap, false otherwise.
* @return {@code true}: success {@code false}: fail
*/
public static boolean save(final Bitmap src,
final String filePath,
final CompressFormat format,
+ final int quality,
final boolean recycle) {
- return save(src, UtilsBridge.getFileByPath(filePath), format, recycle);
+ return save(src, UtilsBridge.getFileByPath(filePath), format, quality, recycle);
}
/**
@@ -1527,14 +1716,27 @@ public static boolean save(final Bitmap src,
* @param src The source of bitmap.
* @param file The file.
* @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
* @param recycle True to recycle the source of bitmap, false otherwise.
* @return {@code true}: success {@code false}: fail
*/
public static boolean save(final Bitmap src,
final File file,
final CompressFormat format,
+ final int quality,
final boolean recycle) {
- if (isEmptyBitmap(src) || !UtilsBridge.createFileByDeleteOldFile(file)) {
+ if (isEmptyBitmap(src)) {
+ Log.e("ImageUtils", "bitmap is empty.");
+ return false;
+ }
+ if (src.isRecycled()) {
+ Log.e("ImageUtils", "bitmap is recycled.");
+ return false;
+ }
+ if (!UtilsBridge.createFileByDeleteOldFile(file)) {
Log.e("ImageUtils", "create or delete file <" + file + "> failed.");
return false;
}
@@ -1542,7 +1744,7 @@ public static boolean save(final Bitmap src,
boolean ret = false;
try {
os = new BufferedOutputStream(new FileOutputStream(file));
- ret = src.compress(format, 100, os);
+ ret = src.compress(format, quality, os);
if (recycle && !src.isRecycled()) src.recycle();
} catch (IOException e) {
e.printStackTrace();
@@ -1558,6 +1760,184 @@ public static boolean save(final Bitmap src,
return ret;
}
+ /**
+ * @param src The source of bitmap.
+ * @param format The format of the image.
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final CompressFormat format) {
+ return save2Album(src, "", format, 100, false);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param format The format of the image.
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final CompressFormat format,
+ final boolean recycle) {
+ return save2Album(src, "", format, 100, recycle);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final CompressFormat format,
+ final int quality) {
+ return save2Album(src, "", format, quality, false);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final CompressFormat format,
+ final int quality,
+ final boolean recycle) {
+ return save2Album(src, "", format, quality, recycle);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param dirName The name of directory.
+ * @param format The format of the image.
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final String dirName,
+ final CompressFormat format) {
+ return save2Album(src, dirName, format, 100, false);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param dirName The name of directory.
+ * @param format The format of the image.
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final String dirName,
+ final CompressFormat format,
+ final boolean recycle) {
+ return save2Album(src, dirName, format, 100, recycle);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param dirName The name of directory.
+ * @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final String dirName,
+ final CompressFormat format,
+ final int quality) {
+ return save2Album(src, dirName, format, quality, false);
+ }
+
+ /**
+ * @param src The source of bitmap.
+ * @param dirName The name of directory.
+ * @param format The format of the image.
+ * @param quality Hint to the compressor, 0-100. 0 meaning compress for
+ * small size, 100 meaning compress for max quality. Some
+ * formats, like PNG which is lossless, will ignore the
+ * quality setting
+ * @param recycle True to recycle the source of bitmap, false otherwise.
+ * @return the file if save success, otherwise return null.
+ */
+ @Nullable
+ public static File save2Album(final Bitmap src,
+ final String dirName,
+ final CompressFormat format,
+ final int quality,
+ final boolean recycle) {
+ String safeDirName = TextUtils.isEmpty(dirName) ? Utils.getApp().getPackageName() : dirName;
+ String suffix = CompressFormat.JPEG.equals(format) ? "JPG" : format.name();
+ String fileName = System.currentTimeMillis() + "_" + quality + "." + suffix;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ if (!UtilsBridge.isGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+ Log.e("ImageUtils", "save to album need storage permission");
+ return null;
+ }
+ File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
+ File destFile = new File(picDir, safeDirName + "/" + fileName);
+ if (!save(src, destFile, format, quality, recycle)) {
+ return null;
+ }
+ UtilsBridge.notifySystemToScan(destFile);
+ return destFile;
+ } else {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
+ contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/*");
+ Uri contentUri;
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else {
+ contentUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
+ }
+ contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/" + safeDirName);
+ contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
+ Uri uri = Utils.getApp().getContentResolver().insert(contentUri, contentValues);
+ if (uri == null) {
+ return null;
+ }
+ OutputStream os = null;
+ try {
+ os = Utils.getApp().getContentResolver().openOutputStream(uri);
+ src.compress(format, quality, os);
+
+ contentValues.clear();
+ contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
+ Utils.getApp().getContentResolver().update(uri, contentValues, null, null);
+
+ return UtilsBridge.uri2File(uri);
+ } catch (Exception e) {
+ Utils.getApp().getContentResolver().delete(uri, null, null);
+ e.printStackTrace();
+ return null;
+ } finally {
+ try {
+ if (os != null) {
+ os.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
/**
* Return whether it is a image according to the file name.
*
@@ -1578,11 +1958,11 @@ public static boolean isImage(final File file) {
* @return {@code true}: yes {@code false}: no
*/
public static boolean isImage(final String filePath) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
try {
- Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
- return bitmap != null && options.outWidth != -1 && options.outHeight != -1;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(filePath, options);
+ return options.outWidth > 0 && options.outHeight > 0;
} catch (Exception e) {
return false;
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java
index 84224db4f9..a5009c3cba 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java
@@ -8,14 +8,17 @@
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
-import android.support.annotation.RequiresPermission;
-import android.support.v4.content.FileProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+import androidx.core.content.FileProvider;
+
import static android.Manifest.permission.CALL_PHONE;
/**
@@ -66,18 +69,33 @@ public static Intent getInstallAppIntent(final String filePath) {
* @return the intent of install app
*/
public static Intent getInstallAppIntent(final File file) {
- if (file == null) return null;
- Intent intent = new Intent(Intent.ACTION_VIEW);
- Uri data;
- String type = "application/vnd.android.package-archive";
+ if (!UtilsBridge.isFileExists(file)) return null;
+ Uri uri;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- data = Uri.fromFile(file);
+ uri = Uri.fromFile(file);
} else {
+ String authority = Utils.getApp().getPackageName() + ".utilcode.fileprovider";
+ uri = FileProvider.getUriForFile(Utils.getApp(), authority, file);
+ }
+ return getInstallAppIntent(uri);
+ }
+
+ /**
+ * Return the intent of install app.
+ *
Target APIs greater than 25 must hold
+ * {@code }
+ *
+ * @param uri The uri.
+ * @return the intent of install app
+ */
+ public static Intent getInstallAppIntent(final Uri uri) {
+ if (uri == null) return null;
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ String type = "application/vnd.android.package-archive";
+ intent.setDataAndType(uri, type);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String authority = Utils.getApp().getPackageName() + ".utilcode.provider";
- data = FileProvider.getUriForFile(Utils.getApp(), authority, file);
}
- intent.setDataAndType(data, type);
return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
@@ -117,9 +135,19 @@ public static Intent getLaunchAppIntent(final String pkgName) {
* @return the intent of launch app details settings
*/
public static Intent getLaunchAppDetailsSettingsIntent(final String pkgName) {
+ return getLaunchAppDetailsSettingsIntent(pkgName, false);
+ }
+
+ /**
+ * Return the intent of launch app details settings.
+ *
+ * @param pkgName The name of the package.
+ * @return the intent of launch app details settings
+ */
+ public static Intent getLaunchAppDetailsSettingsIntent(final String pkgName, final boolean isNewTask) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + pkgName));
- return getIntent(intent, true);
+ return getIntent(intent, isNewTask);
}
/**
@@ -128,14 +156,44 @@ public static Intent getLaunchAppDetailsSettingsIntent(final String pkgName) {
* @param content The content.
* @return the intent of share text
*/
-
public static Intent getShareTextIntent(final String content) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, content);
+ intent = Intent.createChooser(intent, "");
return getIntent(intent, true);
}
+ /**
+ * Return the intent of share image.
+ *
+ * @param imagePath The path of image.
+ * @return the intent of share image
+ */
+ public static Intent getShareImageIntent(final String imagePath) {
+ return getShareTextImageIntent("", imagePath);
+ }
+
+ /**
+ * Return the intent of share image.
+ *
+ * @param imageFile The file of image.
+ * @return the intent of share image
+ */
+ public static Intent getShareImageIntent(final File imageFile) {
+ return getShareTextImageIntent("", imageFile);
+ }
+
+ /**
+ * Return the intent of share image.
+ *
+ * @param imageUri The uri of image.
+ * @return the intent of share image
+ */
+ public static Intent getShareImageIntent(final Uri imageUri) {
+ return getShareTextImageIntent("", imageUri);
+ }
+
/**
* Return the intent of share image.
*
@@ -143,38 +201,67 @@ public static Intent getShareTextIntent(final String content) {
* @param imagePath The path of image.
* @return the intent of share image
*/
- public static Intent getShareImageIntent(final String content, final String imagePath) {
- if (UtilsBridge.isSpace(imagePath)) return null;
- return getShareImageIntent(content, new File(imagePath));
+ public static Intent getShareTextImageIntent(@Nullable final String content, final String imagePath) {
+ return getShareTextImageIntent(content, UtilsBridge.getFileByPath(imagePath));
}
/**
* Return the intent of share image.
*
- * @param content The content.
- * @param image The file of image.
+ * @param content The content.
+ * @param imageFile The file of image.
* @return the intent of share image
*/
- public static Intent getShareImageIntent(final String content, final File image) {
- if (image == null || !image.isFile()) return null;
- return getShareImageIntent(content, UtilsBridge.file2Uri(image));
+ public static Intent getShareTextImageIntent(@Nullable final String content, final File imageFile) {
+ return getShareTextImageIntent(content, UtilsBridge.file2Uri(imageFile));
}
/**
* Return the intent of share image.
*
- * @param content The content.
- * @param uri The uri of image.
+ * @param content The content.
+ * @param imageUri The uri of image.
* @return the intent of share image
*/
- public static Intent getShareImageIntent(final String content, final Uri uri) {
+ public static Intent getShareTextImageIntent(@Nullable final String content, final Uri imageUri) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, content);
- intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.putExtra(Intent.EXTRA_STREAM, imageUri);
intent.setType("image/*");
+ intent = Intent.createChooser(intent, "");
return getIntent(intent, true);
}
+ /**
+ * Return the intent of share images.
+ *
+ * @param imagePaths The paths of images.
+ * @return the intent of share images
+ */
+ public static Intent getShareImageIntent(final LinkedList imagePaths) {
+ return getShareTextImageIntent("", imagePaths);
+ }
+
+ /**
+ * Return the intent of share images.
+ *
+ * @param images The files of images.
+ * @return the intent of share images
+ */
+ public static Intent getShareImageIntent(final List images) {
+ return getShareTextImageIntent("", images);
+ }
+
+ /**
+ * Return the intent of share images.
+ *
+ * @param uris The uris of image.
+ * @return the intent of share image
+ */
+ public static Intent getShareImageIntent(final ArrayList uris) {
+ return getShareTextImageIntent("", uris);
+ }
+
/**
* Return the intent of share images.
*
@@ -182,14 +269,18 @@ public static Intent getShareImageIntent(final String content, final Uri uri) {
* @param imagePaths The paths of images.
* @return the intent of share images
*/
- public static Intent getShareImageIntent(final String content,
- final LinkedList imagePaths) {
- if (imagePaths == null || imagePaths.isEmpty()) return null;
+ public static Intent getShareTextImageIntent(@Nullable final String content,
+ final LinkedList imagePaths) {
List files = new ArrayList<>();
- for (String imagePath : imagePaths) {
- files.add(new File(imagePath));
+ if (imagePaths != null) {
+ for (String imagePath : imagePaths) {
+ File file = UtilsBridge.getFileByPath(imagePath);
+ if (file != null) {
+ files.add(file);
+ }
+ }
}
- return getShareImageIntent(content, files);
+ return getShareTextImageIntent(content, files);
}
/**
@@ -199,14 +290,17 @@ public static Intent getShareImageIntent(final String content,
* @param images The files of images.
* @return the intent of share images
*/
- public static Intent getShareImageIntent(final String content, final List images) {
- if (images == null || images.isEmpty()) return null;
+ public static Intent getShareTextImageIntent(@Nullable final String content, final List images) {
ArrayList uris = new ArrayList<>();
- for (File image : images) {
- if (!image.isFile()) continue;
- uris.add(UtilsBridge.file2Uri(image));
+ if (images != null) {
+ for (File image : images) {
+ Uri uri = UtilsBridge.file2Uri(image);
+ if (uri != null) {
+ uris.add(uri);
+ }
+ }
}
- return getShareImageIntent(content, uris);
+ return getShareTextImageIntent(content, uris);
}
/**
@@ -216,11 +310,12 @@ public static Intent getShareImageIntent(final String content, final List
* @param uris The uris of image.
* @return the intent of share image
*/
- public static Intent getShareImageIntent(final String content, final ArrayList uris) {
+ public static Intent getShareTextImageIntent(@Nullable final String content, final ArrayList uris) {
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.putExtra(Intent.EXTRA_TEXT, content);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
intent.setType("image/*");
+ intent = Intent.createChooser(intent, "");
return getIntent(intent, true);
}
@@ -295,9 +390,9 @@ public static Intent getComponentIntent(final String pkgName,
public static Intent getShutdownIntent() {
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- intent = new Intent(Intent.ACTION_SHUTDOWN);
- } else {
intent = new Intent("com.android.internal.intent.action.REQUEST_SHUTDOWN");
+ } else {
+ intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN");
}
intent.putExtra("android.intent.extra.KEY_CONFIRM", false);
return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -309,8 +404,8 @@ public static Intent getShutdownIntent() {
* @param phoneNumber The phone number.
* @return the intent of dial
*/
- public static Intent getDialIntent(final String phoneNumber) {
- Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
+ public static Intent getDialIntent(@NonNull final String phoneNumber) {
+ Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + Uri.encode(phoneNumber)));
return getIntent(intent, true);
}
@@ -322,8 +417,8 @@ public static Intent getDialIntent(final String phoneNumber) {
* @return the intent of call
*/
@RequiresPermission(CALL_PHONE)
- public static Intent getCallIntent(final String phoneNumber) {
- Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + phoneNumber));
+ public static Intent getCallIntent(@NonNull final String phoneNumber) {
+ Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:" + Uri.encode(phoneNumber)));
return getIntent(intent, true);
}
@@ -334,8 +429,8 @@ public static Intent getCallIntent(final String phoneNumber) {
* @param content The content of SMS.
* @return the intent of send SMS
*/
- public static Intent getSendSmsIntent(final String phoneNumber, final String content) {
- Uri uri = Uri.parse("smsto:" + phoneNumber);
+ public static Intent getSendSmsIntent(@NonNull final String phoneNumber, final String content) {
+ Uri uri = Uri.parse("smsto:" + Uri.encode(phoneNumber));
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", content);
return getIntent(intent, true);
@@ -348,10 +443,21 @@ public static Intent getSendSmsIntent(final String phoneNumber, final String con
* @return the intent of capture
*/
public static Intent getCaptureIntent(final Uri outUri) {
+ return getCaptureIntent(outUri, false);
+ }
+
+ /**
+ * Return the intent of capture.
+ *
+ * @param outUri The uri of output.
+ * @param isNewTask True to add flag of new task, false otherwise.
+ * @return the intent of capture
+ */
+ public static Intent getCaptureIntent(final Uri outUri, final boolean isNewTask) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- return getIntent(intent, true);
+ return getIntent(intent, isNewTask);
}
private static Intent getIntent(final Intent intent, final boolean isNewTask) {
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/JsonUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/JsonUtils.java
index 5945b05807..43046adf5a 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/JsonUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/JsonUtils.java
@@ -1,7 +1,5 @@
package com.blankj.utilcode.util;
-import android.util.Log;
-
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -28,6 +26,27 @@ private JsonUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
+
+ /**
+ * Checks if a given input is a JSONObject.
+ *
+ * @param input Anything.
+ * @return true if it is a JSONObject.
+ */
+ public static boolean isJSONObject(final T input) {
+ return input instanceof JSONObject;
+ }
+
+ /**
+ * Checks if a given input is a JSONArray
+ *
+ * @param input Anything.
+ * @return true if it is a JSONArray.
+ */
+ public static boolean isJSONArray(final T input) {
+ return input instanceof JSONArray;
+ }
+
public static boolean getBoolean(final JSONObject jsonObject,
final String key) {
return getBoolean(jsonObject, key, false);
@@ -191,7 +210,7 @@ private static T getValueByType(final JSONObject jsonObject,
//noinspection unchecked
return (T) ret;
} catch (JSONException e) {
- Log.e("JsonUtils", "getValueByType: ", e);
+ e.printStackTrace();
return defaultValue;
}
}
@@ -207,7 +226,7 @@ private static T getValueByType(final String json,
try {
return getValueByType(new JSONObject(json), key, defaultValue, type);
} catch (JSONException e) {
- Log.e("JsonUtils", "getValueByType: ", e);
+ e.printStackTrace();
return defaultValue;
}
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java
index 7ab11b7981..e51b3cf891 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java
@@ -7,7 +7,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
-import android.support.annotation.NonNull;
+import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -17,7 +17,8 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
-
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.lang.reflect.Field;
/**
@@ -40,7 +41,8 @@ private KeyboardUtils() {
* Show the soft input.
*/
public static void showSoftInput() {
- InputMethodManager imm = (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
+ InputMethodManager imm =
+ (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm == null) {
return;
}
@@ -50,7 +52,10 @@ public static void showSoftInput() {
/**
* Show the soft input.
*/
- public static void showSoftInput(@NonNull Activity activity) {
+ public static void showSoftInput(@Nullable Activity activity) {
+ if (activity == null) {
+ return;
+ }
if (!isSoftInputVisible(activity)) {
toggleSoftInput();
}
@@ -68,14 +73,16 @@ public static void showSoftInput(@NonNull final View view) {
/**
* Show the soft input.
*
- * @param view The view.
+ * @param view The view.
* @param flags Provides additional operating flags. Currently may be
- * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} bit set.
+ * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} bit set.
*/
public static void showSoftInput(@NonNull final View view, final int flags) {
InputMethodManager imm =
- (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm == null) return;
+ (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm == null) {
+ return;
+ }
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.requestFocus();
@@ -83,7 +90,7 @@ public static void showSoftInput(@NonNull final View view, final int flags) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == InputMethodManager.RESULT_UNCHANGED_HIDDEN
- || resultCode == InputMethodManager.RESULT_HIDDEN) {
+ || resultCode == InputMethodManager.RESULT_HIDDEN) {
toggleSoftInput();
}
}
@@ -96,15 +103,30 @@ protected void onReceiveResult(int resultCode, Bundle resultData) {
*
* @param activity The activity.
*/
- public static void hideSoftInput(@NonNull final Activity activity) {
- View view = activity.getCurrentFocus();
+ public static void hideSoftInput(@Nullable final Activity activity) {
+ if (activity == null) {
+ return;
+ }
+ hideSoftInput(activity.getWindow());
+ }
+
+ /**
+ * Hide the soft input.
+ *
+ * @param window The window.
+ */
+ public static void hideSoftInput(@Nullable final Window window) {
+ if (window == null) {
+ return;
+ }
+ View view = window.getCurrentFocus();
if (view == null) {
- View decorView = activity.getWindow().getDecorView();
+ View decorView = window.getDecorView();
View focusView = decorView.findViewWithTag("keyboardTagView");
if (focusView == null) {
- view = new EditText(activity);
+ view = new EditText(window.getContext());
view.setTag("keyboardTagView");
- ((ViewGroup) decorView).addView(view, 1, 1);
+ ((ViewGroup) decorView).addView(view, 0, 0);
} else {
view = focusView;
}
@@ -120,8 +142,10 @@ public static void hideSoftInput(@NonNull final Activity activity) {
*/
public static void hideSoftInput(@NonNull final View view) {
InputMethodManager imm =
- (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm == null) return;
+ (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm == null) {
+ return;
+ }
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
@@ -132,10 +156,13 @@ public static void hideSoftInput(@NonNull final View view) {
*
* @param activity The activity.
*/
- public static void hideSoftInputByToggle(final Activity activity) {
- long nowMillis = System.currentTimeMillis();
+ public static void hideSoftInputByToggle(@Nullable final Activity activity) {
+ if (activity == null) {
+ return;
+ }
+ long nowMillis = SystemClock.elapsedRealtime();
long delta = nowMillis - millis;
- if (KeyboardUtils.isSoftInputVisible(activity) && Math.abs(delta) > 500) {
+ if (Math.abs(delta) > 500 && KeyboardUtils.isSoftInputVisible(activity)) {
KeyboardUtils.toggleSoftInput();
}
millis = nowMillis;
@@ -146,8 +173,10 @@ public static void hideSoftInputByToggle(final Activity activity) {
*/
public static void toggleSoftInput() {
InputMethodManager imm =
- (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm == null) return;
+ (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm == null) {
+ return;
+ }
imm.toggleSoftInput(0, 0);
}
@@ -167,8 +196,8 @@ private static int getDecorViewInvisibleHeight(@NonNull final Window window) {
final View decorView = window.getDecorView();
final Rect outRect = new Rect();
decorView.getWindowVisibleDisplayFrame(outRect);
- Log.d("KeyboardUtils", "getDecorViewInvisibleHeight: "
- + (decorView.getBottom() - outRect.bottom));
+ Log.d("KeyboardUtils",
+ "getDecorViewInvisibleHeight: " + (decorView.getBottom() - outRect.bottom));
int delta = Math.abs(decorView.getBottom() - outRect.bottom);
if (delta <= UtilsBridge.getNavBarHeight() + UtilsBridge.getStatusBarHeight()) {
sDecorViewDelta = delta;
@@ -184,24 +213,26 @@ private static int getDecorViewInvisibleHeight(@NonNull final Window window) {
* @param listener The soft input changed listener.
*/
public static void registerSoftInputChangedListener(@NonNull final Activity activity,
- @NonNull final OnSoftInputChangedListener listener) {
+ @NonNull
+ final OnSoftInputChangedListener listener) {
registerSoftInputChangedListener(activity.getWindow(), listener);
}
/**
* Register soft input changed listener.
*
- * @param window The window.
+ * @param window The window.
* @param listener The soft input changed listener.
*/
public static void registerSoftInputChangedListener(@NonNull final Window window,
- @NonNull final OnSoftInputChangedListener listener) {
+ @NonNull
+ final OnSoftInputChangedListener listener) {
final int flags = window.getAttributes().flags;
if ((flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) != 0) {
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
final FrameLayout contentView = window.findViewById(android.R.id.content);
- final int[] decorViewInvisibleHeightPre = {getDecorViewInvisibleHeight(window)};
+ final int[] decorViewInvisibleHeightPre = { getDecorViewInvisibleHeight(window) };
OnGlobalLayoutListener onGlobalLayoutListener = new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
@@ -222,11 +253,16 @@ public void onGlobalLayout() {
* @param window The window.
*/
public static void unregisterSoftInputChangedListener(@NonNull final Window window) {
- final FrameLayout contentView = window.findViewById(android.R.id.content);
+ final View contentView = window.findViewById(android.R.id.content);
+ if (contentView == null) {
+ return;
+ }
Object tag = contentView.getTag(TAG_ON_GLOBAL_LAYOUT_LISTENER);
if (tag instanceof OnGlobalLayoutListener) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
contentView.getViewTreeObserver().removeOnGlobalLayoutListener((OnGlobalLayoutListener) tag);
+ //这里会发生内存泄漏 如果不设置为null
+ contentView.setTag(TAG_ON_GLOBAL_LAYOUT_LISTENER, null);
}
}
}
@@ -249,36 +285,35 @@ public static void fixAndroidBug5497(@NonNull final Activity activity) {
*/
public static void fixAndroidBug5497(@NonNull final Window window) {
int softInputMode = window.getAttributes().softInputMode;
- window.setSoftInputMode(softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+ window.setSoftInputMode(
+ softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
final FrameLayout contentView = window.findViewById(android.R.id.content);
final View contentViewChild = contentView.getChildAt(0);
final int paddingBottom = contentViewChild.getPaddingBottom();
- final int[] contentViewInvisibleHeightPre5497 = {getContentViewInvisibleHeight(window)};
- contentView.getViewTreeObserver()
- .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- int height = getContentViewInvisibleHeight(window);
- if (contentViewInvisibleHeightPre5497[0] != height) {
- contentViewChild.setPadding(
- contentViewChild.getPaddingLeft(),
- contentViewChild.getPaddingTop(),
- contentViewChild.getPaddingRight(),
- paddingBottom + getDecorViewInvisibleHeight(window)
- );
- contentViewInvisibleHeightPre5497[0] = height;
- }
- }
- });
+ final int[] contentViewInvisibleHeightPre5497 = { getContentViewInvisibleHeight(window) };
+ contentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ int height = getContentViewInvisibleHeight(window);
+ if (contentViewInvisibleHeightPre5497[0] != height) {
+ contentViewChild.setPadding(contentViewChild.getPaddingLeft(),
+ contentViewChild.getPaddingTop(), contentViewChild.getPaddingRight(),
+ paddingBottom + getDecorViewInvisibleHeight(window));
+ contentViewInvisibleHeightPre5497[0] = height;
+ }
+ }
+ });
}
private static int getContentViewInvisibleHeight(final Window window) {
final View contentView = window.findViewById(android.R.id.content);
- if (contentView == null) return 0;
+ if (contentView == null) {
+ return 0;
+ }
final Rect outRect = new Rect();
contentView.getWindowVisibleDisplayFrame(outRect);
- Log.d("KeyboardUtils", "getContentViewInvisibleHeight: "
- + (contentView.getBottom() - outRect.bottom));
+ Log.d("KeyboardUtils",
+ "getContentViewInvisibleHeight: " + (contentView.getBottom() - outRect.bottom));
int delta = Math.abs(contentView.getBottom() - outRect.bottom);
if (delta <= UtilsBridge.getStatusBarHeight() + UtilsBridge.getNavBarHeight()) {
return 0;
@@ -302,9 +337,12 @@ public static void fixSoftInputLeaks(@NonNull final Activity activity) {
*/
public static void fixSoftInputLeaks(@NonNull final Window window) {
InputMethodManager imm =
- (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm == null) return;
- String[] leakViews = new String[]{"mLastSrvView", "mCurRootView", "mServedView", "mNextServedView"};
+ (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm == null) {
+ return;
+ }
+ String[] leakViews =
+ new String[] { "mLastSrvView", "mCurRootView", "mServedView", "mNextServedView" };
for (String leakView : leakViews) {
try {
Field leakViewField = InputMethodManager.class.getDeclaredField(leakView);
@@ -312,7 +350,9 @@ public static void fixSoftInputLeaks(@NonNull final Window window) {
leakViewField.setAccessible(true);
}
Object obj = leakViewField.get(imm);
- if (!(obj instanceof View)) continue;
+ if (!(obj instanceof View)) {
+ continue;
+ }
View view = (View) obj;
if (view.getRootView() == window.getDecorView().getRootView()) {
leakViewField.set(imm, null);
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java
index f73dc14828..ca463854b2 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java
@@ -1,23 +1,18 @@
package com.blankj.utilcode.util;
import android.app.Activity;
-import android.app.Application;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
-import java.lang.reflect.Field;
import java.util.Locale;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
*
* author: Blankj
@@ -37,178 +32,287 @@ private LanguageUtils() {
/**
* Apply the system language.
- * It will not restart Activity. u can put it in ur {@link Activity#onCreate(Bundle)}.
*/
public static void applySystemLanguage() {
- if (isAppliedSystemLanguage()) return;
- applyLanguage(Resources.getSystem().getConfiguration().locale, "", true, false);
- }
-
- /**
- * Apply the system language.
- *
- * @param activityClz The class of activity will be started after apply system language.
- */
- public static void applySystemLanguage(final Class extends Activity> activityClz) {
- applyLanguage(Resources.getSystem().getConfiguration().locale, activityClz, true, true);
+ applySystemLanguage(false);
}
/**
* Apply the system language.
*
- * @param activityClassName The full class name of activity will be started after apply system language.
+ * @param isRelaunchApp True to relaunch app, false to recreate all activities.
*/
- public static void applySystemLanguage(final String activityClassName) {
- applyLanguage(Resources.getSystem().getConfiguration().locale, activityClassName, true, true);
+ public static void applySystemLanguage(final boolean isRelaunchApp) {
+ applyLanguageReal(null, isRelaunchApp);
}
/**
* Apply the language.
- * It will not restart Activity. u can put it in ur {@link Activity#onCreate(Bundle)}.
*
* @param locale The language of locale.
*/
public static void applyLanguage(@NonNull final Locale locale) {
- if (isAppliedLanguage()) return;
- applyLanguage(locale, "", false, false);
+ applyLanguage(locale, false);
}
/**
* Apply the language.
*
- * @param locale The language of locale.
- * @param activityClz The class of activity will be started after apply system language.
- * It will start the launcher activity if the class is null.
+ * @param locale The language of locale.
+ * @param isRelaunchApp True to relaunch app, false to recreate all activities.
*/
public static void applyLanguage(@NonNull final Locale locale,
- final Class extends Activity> activityClz) {
- applyLanguage(locale, activityClz, false, true);
+ final boolean isRelaunchApp) {
+ applyLanguageReal(locale, isRelaunchApp);
+ }
+
+ private static void applyLanguageReal(final Locale locale,
+ final boolean isRelaunchApp) {
+ if (locale == null) {
+ UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, VALUE_FOLLOW_SYSTEM, true);
+ } else {
+ UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, locale2String(locale), true);
+ }
+
+ Locale destLocal = locale == null ? getLocal(Resources.getSystem().getConfiguration()) : locale;
+ updateAppContextLanguage(destLocal, new Utils.Consumer() {
+ @Override
+ public void accept(Boolean success) {
+ if (success) {
+ restart(isRelaunchApp);
+ } else {
+ // use relaunch app
+ UtilsBridge.relaunchApp();
+ }
+ }
+ });
+ }
+
+ private static void restart(final boolean isRelaunchApp) {
+ if (isRelaunchApp) {
+ UtilsBridge.relaunchApp();
+ } else {
+ for (Activity activity : UtilsBridge.getActivityList()) {
+ activity.recreate();
+ }
+ }
}
/**
- * Apply the language.
+ * Return whether applied the language by {@link LanguageUtils}.
*
- * @param locale The language of locale.
- * @param activityClassName The class of activity will be started after apply system language.
- * It will start the launcher activity if the class name is null.
+ * @return {@code true}: yes {@code false}: no
*/
- public static void applyLanguage(@NonNull final Locale locale,
- final String activityClassName) {
- applyLanguage(locale, activityClassName, false, true);
+ public static boolean isAppliedLanguage() {
+ return getAppliedLanguage() != null;
}
- private static void applyLanguage(@NonNull final Locale locale,
- final Class extends Activity> activityClz,
- final boolean isFollowSystem,
- final boolean isNeedStartActivity) {
- if (activityClz == null) {
- applyLanguage(locale, "", isFollowSystem, isNeedStartActivity);
- return;
+ /**
+ * Return whether applied the language by {@link LanguageUtils}.
+ *
+ * @param locale The locale.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isAppliedLanguage(@NonNull Locale locale) {
+ Locale appliedLocale = getAppliedLanguage();
+ if (appliedLocale == null) {
+ return false;
}
- applyLanguage(locale, activityClz.getName(), isFollowSystem, isNeedStartActivity);
+ return isSameLocale(locale, appliedLocale);
}
- private static void applyLanguage(@NonNull final Locale locale,
- final String activityClassName,
- final boolean isFollowSystem,
- final boolean isNeedStartActivity) {
- if (isFollowSystem) {
- UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, VALUE_FOLLOW_SYSTEM);
- } else {
- String localLanguage = locale.getLanguage();
- String localCountry = locale.getCountry();
- UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, localLanguage + "$" + localCountry);
+ /**
+ * Return the applied locale.
+ *
+ * @return the applied locale
+ */
+ public static Locale getAppliedLanguage() {
+ final String spLocaleStr = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);
+ if (TextUtils.isEmpty(spLocaleStr) || VALUE_FOLLOW_SYSTEM.equals(spLocaleStr)) {
+ return null;
}
+ return string2Locale(spLocaleStr);
+ }
- updateLanguage(Utils.getApp(), locale);
+ /**
+ * Return the locale of context.
+ *
+ * @return the locale of context
+ */
+ public static Locale getContextLanguage(Context context) {
+ return getLocal(context.getResources().getConfiguration());
+ }
- if (isNeedStartActivity) {
- Intent intent = new Intent();
- String realActivityClassName = TextUtils.isEmpty(activityClassName) ? UtilsBridge.getLauncherActivity() : activityClassName;
- intent.setComponent(new ComponentName(Utils.getApp(), realActivityClassName));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- Utils.getApp().startActivity(intent);
- }
+ /**
+ * Return the locale of applicationContext.
+ *
+ * @return the locale of applicationContext
+ */
+ public static Locale getAppContextLanguage() {
+ return getContextLanguage(Utils.getApp());
}
/**
- * Return whether applied the system language by {@link LanguageUtils}.
+ * Return the locale of system
*
- * @return {@code true}: yes {@code false}: no
+ * @return the locale of system
*/
- public static boolean isAppliedSystemLanguage() {
- return VALUE_FOLLOW_SYSTEM.equals(UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE));
+ public static Locale getSystemLanguage() {
+ return getLocal(Resources.getSystem().getConfiguration());
}
/**
- * Return whether applied the language by {@link LanguageUtils}.
+ * Update the locale of applicationContext.
*
- * @return {@code true}: yes {@code false}: no
+ * @param destLocale The dest locale.
+ * @param consumer The consumer.
*/
- public static boolean isAppliedLanguage() {
- return !TextUtils.isEmpty(UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE));
+ public static void updateAppContextLanguage(@NonNull Locale destLocale, @Nullable Utils.Consumer consumer) {
+ pollCheckAppContextLocal(destLocale, 0, consumer);
+ }
+
+ static void pollCheckAppContextLocal(final Locale destLocale, final int index, final Utils.Consumer consumer) {
+ Resources appResources = Utils.getApp().getResources();
+ Configuration appConfig = appResources.getConfiguration();
+ Locale appLocal = getLocal(appConfig);
+
+ setLocal(appConfig, destLocale);
+
+ Utils.getApp().getResources().updateConfiguration(appConfig, appResources.getDisplayMetrics());
+
+ if (consumer == null) return;
+
+ if (isSameLocale(appLocal, destLocale)) {
+ consumer.accept(true);
+ } else {
+ if (index < 20) {
+ UtilsBridge.runOnUiThreadDelayed(new Runnable() {
+ @Override
+ public void run() {
+ pollCheckAppContextLocal(destLocale, index + 1, consumer);
+ }
+ }, 16);
+ return;
+ }
+ Log.e("LanguageUtils", "appLocal didn't update.");
+ consumer.accept(false);
+ }
}
/**
- * Return the locale.
+ * If applyLanguage not work, try to call it in {@link Activity#attachBaseContext(Context)}.
*
- * @return the locale
+ * @param context The baseContext.
+ * @return the context with language
*/
- public static Locale getCurrentLocale() {
- return Utils.getApp().getResources().getConfiguration().locale;
+ public static Context attachBaseContext(Context context) {
+ String spLocaleStr = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);
+ if (TextUtils.isEmpty(spLocaleStr) || VALUE_FOLLOW_SYSTEM.equals(spLocaleStr)) {
+ return context;
+ }
+
+ Locale settingsLocale = string2Locale(spLocaleStr);
+ if (settingsLocale == null) return context;
+
+ Resources resources = context.getResources();
+ Configuration config = resources.getConfiguration();
+
+ setLocal(config, settingsLocale);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return context.createConfigurationContext(config);
+ } else {
+ resources.updateConfiguration(config, resources.getDisplayMetrics());
+ return context;
+ }
}
- static void applyLanguage(@NonNull final Activity activity) {
- final String spLocale = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);
+ static void applyLanguage(final Activity activity) {
+ String spLocale = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);
if (TextUtils.isEmpty(spLocale)) {
return;
}
+ Locale destLocal;
if (VALUE_FOLLOW_SYSTEM.equals(spLocale)) {
- Locale sysLocale = Resources.getSystem().getConfiguration().locale;
- updateLanguage(Utils.getApp(), sysLocale);
- updateLanguage(activity, sysLocale);
- return;
+ destLocal = getLocal(Resources.getSystem().getConfiguration());
+ } else {
+ destLocal = string2Locale(spLocale);
}
- String[] language_country = spLocale.split("\\$");
- if (language_country.length != 2) {
- Log.e("LanguageUtils", "The string of " + spLocale + " is not in the correct format.");
- return;
- }
+ if (destLocal == null) return;
- Locale settingLocale = new Locale(language_country[0], language_country[1]);
- updateLanguage(Utils.getApp(), settingLocale);
- updateLanguage(activity, settingLocale);
+ updateConfiguration(activity, destLocal);
+ updateConfiguration(Utils.getApp(), destLocal);
}
- private static void updateLanguage(final Context context, Locale locale) {
+ private static void updateConfiguration(Context context, Locale destLocal) {
Resources resources = context.getResources();
Configuration config = resources.getConfiguration();
- Locale contextLocale = config.locale;
- if (isSameLocale(contextLocale, locale)) {
- return;
+ setLocal(config, destLocal);
+ resources.updateConfiguration(config, resources.getDisplayMetrics());
+ }
+
+ private static String locale2String(Locale locale) {
+ String localLanguage = locale.getLanguage(); // this may be empty
+ String localCountry = locale.getCountry(); // this may be empty
+ return localLanguage + "$" + localCountry;
+ }
+
+ private static Locale string2Locale(String str) {
+ Locale locale = string2LocaleReal(str);
+ if (locale == null) {
+ Log.e("LanguageUtils", "The string of " + str + " is not in the correct format.");
+ UtilsBridge.getSpUtils4Utils().remove(KEY_LOCALE);
}
- DisplayMetrics dm = resources.getDisplayMetrics();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- config.setLocale(locale);
- if (context instanceof Application) {
- Context newContext = context.createConfigurationContext(config);
- try {
- //noinspection JavaReflectionMemberAccess
- Field mBaseField = ContextWrapper.class.getDeclaredField("mBase");
- mBaseField.setAccessible(true);
- mBaseField.set(context, newContext);
- } catch (Exception ignored) {/**/}
+ return locale;
+ }
+
+ private static Locale string2LocaleReal(String str) {
+ if (!isRightFormatLocalStr(str)) {
+ return null;
+ }
+
+ try {
+ int splitIndex = str.indexOf("$");
+ return new Locale(str.substring(0, splitIndex), str.substring(splitIndex + 1));
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ private static boolean isRightFormatLocalStr(String localStr) {
+ char[] chars = localStr.toCharArray();
+ int count = 0;
+ for (char c : chars) {
+ if (c == '$') {
+ if (count >= 1) {
+ return false;
+ }
+ ++count;
}
+ }
+ return count == 1;
+ }
+
+ private static boolean isSameLocale(Locale l0, Locale l1) {
+ return UtilsBridge.equals(l1.getLanguage(), l0.getLanguage())
+ && UtilsBridge.equals(l1.getCountry(), l0.getCountry());
+ }
+
+ private static Locale getLocal(Configuration configuration) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return configuration.getLocales().get(0);
} else {
- config.locale = locale;
+ return configuration.locale;
}
- resources.updateConfiguration(config, dm);
}
- private static boolean isSameLocale(Locale locale, Locale contextLocale) {
- return UtilsBridge.equals(contextLocale.getLanguage(), locale.getLanguage())
- && UtilsBridge.equals(contextLocale.getCountry(), locale.getCountry());
+ private static void setLocal(Configuration configuration, Locale locale) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ configuration.setLocale(locale);
+ } else {
+ configuration.locale = locale;
+ }
}
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java
index 040a3545d0..128e2b8de6 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java
@@ -7,10 +7,6 @@
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.IntDef;
-import android.support.annotation.IntRange;
-import android.support.annotation.RequiresApi;
-import android.support.v4.util.SimpleArrayMap;
import android.util.Log;
import org.json.JSONArray;
@@ -36,6 +32,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -49,6 +46,11 @@
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
+import androidx.annotation.IntDef;
+import androidx.annotation.IntRange;
+import androidx.annotation.RequiresApi;
+import androidx.collection.SimpleArrayMap;
+
/**
*
* author: Blankj
@@ -228,6 +230,10 @@ public void run() {
}
}
+ public static String getCurrentLogFilePath() {
+ return getCurrentLogFilePath(new Date());
+ }
+
public static List getLogFiles() {
String dir = CONFIG.getDir();
File logDir = new File(dir);
@@ -379,16 +385,16 @@ private static void print2Console(final int type,
private static void printBorder(final int type, final String tag, boolean isTop) {
if (CONFIG.isLogBorderSwitch()) {
- Log.println(type, tag, isTop ? TOP_BORDER : BOTTOM_BORDER);
+ print2Console(type, tag, isTop ? TOP_BORDER : BOTTOM_BORDER);
}
}
private static void printHead(final int type, final String tag, final String[] head) {
if (head != null) {
for (String aHead : head) {
- Log.println(type, tag, CONFIG.isLogBorderSwitch() ? LEFT_BORDER + aHead : aHead);
+ print2Console(type, tag, CONFIG.isLogBorderSwitch() ? LEFT_BORDER + aHead : aHead);
}
- if (CONFIG.isLogBorderSwitch()) Log.println(type, tag, MIDDLE_BORDER);
+ if (CONFIG.isLogBorderSwitch()) print2Console(type, tag, MIDDLE_BORDER);
}
}
@@ -411,13 +417,13 @@ private static void printMsg(final int type, final String tag, final String msg)
private static void printSubMsg(final int type, final String tag, final String msg) {
if (!CONFIG.isLogBorderSwitch()) {
- Log.println(type, tag, msg);
+ print2Console(type, tag, msg);
return;
}
StringBuilder sb = new StringBuilder();
String[] lines = msg.split(LINE_SEP);
for (String line : lines) {
- Log.println(type, tag, LEFT_BORDER + line);
+ print2Console(type, tag, LEFT_BORDER + line);
}
}
@@ -456,56 +462,70 @@ private static void printSingleTagMsg(final int type, final String tag, final St
int countOfSub = CONFIG.isLogBorderSwitch() ? (len - BOTTOM_BORDER.length()) / MAX_LEN : len / MAX_LEN;
if (countOfSub > 0) {
if (CONFIG.isLogBorderSwitch()) {
- Log.println(type, tag, msg.substring(0, MAX_LEN) + LINE_SEP + BOTTOM_BORDER);
+ print2Console(type, tag, msg.substring(0, MAX_LEN) + LINE_SEP + BOTTOM_BORDER);
int index = MAX_LEN;
for (int i = 1; i < countOfSub; i++) {
- Log.println(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
+ print2Console(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
+ LEFT_BORDER + msg.substring(index, index + MAX_LEN)
+ LINE_SEP + BOTTOM_BORDER);
index += MAX_LEN;
}
if (index != len - BOTTOM_BORDER.length()) {
- Log.println(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
+ print2Console(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP
+ LEFT_BORDER + msg.substring(index, len));
}
} else {
- Log.println(type, tag, msg.substring(0, MAX_LEN));
+ print2Console(type, tag, msg.substring(0, MAX_LEN));
int index = MAX_LEN;
for (int i = 1; i < countOfSub; i++) {
- Log.println(type, tag,
+ print2Console(type, tag,
PLACEHOLDER + LINE_SEP + msg.substring(index, index + MAX_LEN));
index += MAX_LEN;
}
if (index != len) {
- Log.println(type, tag, PLACEHOLDER + LINE_SEP + msg.substring(index, len));
+ print2Console(type, tag, PLACEHOLDER + LINE_SEP + msg.substring(index, len));
}
}
} else {
- Log.println(type, tag, msg);
+ print2Console(type, tag, msg);
+ }
+ }
+
+ private static void print2Console(int type, String tag, String msg) {
+ Log.println(type, tag, msg);
+ if (CONFIG.mOnConsoleOutputListener != null) {
+ CONFIG.mOnConsoleOutputListener.onConsoleOutput(type, tag, msg);
}
}
private static void print2File(final int type, final String tag, final String msg) {
- String format = getSdf().format(new Date());
+ Date d = new Date();
+ String format = getSdf().format(d);
String date = format.substring(0, 10);
- String time = format.substring(11);
- final String fullPath =
- CONFIG.getDir() + CONFIG.getFilePrefix() + "_"
- + date + "_" +
- CONFIG.getProcessName() + CONFIG.getFileExtension();
- if (!createOrExistsFile(fullPath, date)) {
- Log.e("LogUtils", "create " + fullPath + " failed!");
+ String currentLogFilePath = getCurrentLogFilePath(d);
+ if (!createOrExistsFile(currentLogFilePath, date)) {
+ Log.e("LogUtils", "create " + currentLogFilePath + " failed!");
return;
}
+ String time = format.substring(11);
final String content = time +
T[type - V] +
"/" +
tag +
msg +
LINE_SEP;
- input2File(fullPath, content);
+ input2File(currentLogFilePath, content);
}
+ private static String getCurrentLogFilePath(Date d) {
+ String format = getSdf().format(d);
+ String date = format.substring(0, 10);
+ return CONFIG.getDir() + CONFIG.getFilePrefix() + "_"
+ + date + "_" +
+ CONFIG.getProcessName() + CONFIG.getFileExtension();
+ }
+
+
private static SimpleDateFormat getSdf() {
if (simpleDateFormat == null) {
simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd HH:mm:ss.SSS ", Locale.getDefault());
@@ -579,16 +599,8 @@ private static String findDate(String str) {
}
private static void printDeviceInfo(final String filePath, final String date) {
- final String head = "************* Log Head ****************" +
- "\nDate of Log : " + date +
- "\nDevice Manufacturer: " + Build.MANUFACTURER +
- "\nDevice Model : " + Build.MODEL +
- "\nAndroid Version : " + Build.VERSION.RELEASE +
- "\nAndroid SDK : " + Build.VERSION.SDK_INT +
- "\nApp VersionName : " + UtilsBridge.getAppVersionName() +
- "\nApp VersionCode : " + UtilsBridge.getAppVersionCode() +
- "\n************* Log Head ****************\n\n";
- input2File(filePath, head);
+ CONFIG.mFileHead.addFirst("Date of Log", date);
+ input2File(filePath, CONFIG.mFileHead.toString());
}
private static void input2File(final String filePath, final String input) {
@@ -597,28 +609,34 @@ private static void input2File(final String filePath, final String input) {
} else {
CONFIG.mFileWriter.write(filePath, input);
}
+ if (CONFIG.mOnFileOutputListener != null) {
+ CONFIG.mOnFileOutputListener.onFileOutput(filePath, input);
+ }
}
public static final class Config {
- private String mDefaultDir;// The default storage directory of log.
- private String mDir; // The storage directory of log.
- private String mFilePrefix = "util";// The file prefix of log.
- private String mFileExtension = ".txt";// The file extension of log.
- private boolean mLogSwitch = true; // The switch of log.
- private boolean mLog2ConsoleSwitch = true; // The logcat's switch of log.
- private String mGlobalTag = ""; // The global tag of log.
- private boolean mTagIsSpace = true; // The global tag is space.
- private boolean mLogHeadSwitch = true; // The head's switch of log.
- private boolean mLog2FileSwitch = false; // The file's switch of log.
- private boolean mLogBorderSwitch = true; // The border's switch of log.
- private boolean mSingleTagSwitch = true; // The single tag of log.
- private int mConsoleFilter = V; // The console's filter of log.
- private int mFileFilter = V; // The file's filter of log.
- private int mStackDeep = 1; // The stack's deep of log.
- private int mStackOffset = 0; // The stack's offset of log.
- private int mSaveDays = -1; // The save days of log.
- private String mProcessName = UtilsBridge.getCurrentProcessName();
- private IFileWriter mFileWriter;
+ private String mDefaultDir; // The default storage directory of log.
+ private String mDir; // The storage directory of log.
+ private String mFilePrefix = "util";// The file prefix of log.
+ private String mFileExtension = ".txt";// The file extension of log.
+ private boolean mLogSwitch = true; // The switch of log.
+ private boolean mLog2ConsoleSwitch = true; // The logcat's switch of log.
+ private String mGlobalTag = ""; // The global tag of log.
+ private boolean mTagIsSpace = true; // The global tag is space.
+ private boolean mLogHeadSwitch = true; // The head's switch of log.
+ private boolean mLog2FileSwitch = false; // The file's switch of log.
+ private boolean mLogBorderSwitch = true; // The border's switch of log.
+ private boolean mSingleTagSwitch = true; // The single tag of log.
+ private int mConsoleFilter = V; // The console's filter of log.
+ private int mFileFilter = V; // The file's filter of log.
+ private int mStackDeep = 1; // The stack's deep of log.
+ private int mStackOffset = 0; // The stack's offset of log.
+ private int mSaveDays = -1; // The save days of log.
+ private String mProcessName = UtilsBridge.getCurrentProcessName();
+ private IFileWriter mFileWriter;
+ private OnConsoleOutputListener mOnConsoleOutputListener;
+ private OnFileOutputListener mOnFileOutputListener;
+ private UtilsBridge.FileHead mFileHead = new UtilsBridge.FileHead("Log");
private Config() {
if (UtilsBridge.isSDCardEnableByEnvironment()
@@ -743,6 +761,26 @@ public final Config setFileWriter(final IFileWriter fileWriter) {
return this;
}
+ public final Config setOnConsoleOutputListener(final OnConsoleOutputListener listener) {
+ mOnConsoleOutputListener = listener;
+ return this;
+ }
+
+ public final Config setOnFileOutputListener(final OnFileOutputListener listener) {
+ mOnFileOutputListener = listener;
+ return this;
+ }
+
+ public final Config addFileExtraHead(final Map fileExtraHead) {
+ mFileHead.append(fileExtraHead);
+ return this;
+ }
+
+ public final Config addFileExtraHead(final String key, final String value) {
+ mFileHead.append(key, value);
+ return this;
+ }
+
public final String getProcessName() {
if (mProcessName == null) return "";
return mProcessName.replace(":", "_");
@@ -813,24 +851,36 @@ public final int getSaveDays() {
return mSaveDays;
}
+ public final boolean haveSetOnConsoleOutputListener() {
+ return mOnConsoleOutputListener != null;
+ }
+
+ public final boolean haveSetOnFileOutputListener() {
+ return mOnFileOutputListener != null;
+ }
+
@Override
public String toString() {
return "process: " + getProcessName()
- + LINE_SEP + "switch: " + isLogSwitch()
- + LINE_SEP + "console: " + isLog2ConsoleSwitch()
- + LINE_SEP + "tag: " + getGlobalTag()
- + LINE_SEP + "head: " + isLogHeadSwitch()
- + LINE_SEP + "file: " + isLog2FileSwitch()
+ + LINE_SEP + "logSwitch: " + isLogSwitch()
+ + LINE_SEP + "consoleSwitch: " + isLog2ConsoleSwitch()
+ + LINE_SEP + "tag: " + (getGlobalTag().equals("") ? "null" : getGlobalTag())
+ + LINE_SEP + "headSwitch: " + isLogHeadSwitch()
+ + LINE_SEP + "fileSwitch: " + isLog2FileSwitch()
+ LINE_SEP + "dir: " + getDir()
+ LINE_SEP + "filePrefix: " + getFilePrefix()
- + LINE_SEP + "border: " + isLogBorderSwitch()
- + LINE_SEP + "singleTag: " + isSingleTagSwitch()
+ + LINE_SEP + "borderSwitch: " + isLogBorderSwitch()
+ + LINE_SEP + "singleTagSwitch: " + isSingleTagSwitch()
+ LINE_SEP + "consoleFilter: " + getConsoleFilter()
+ LINE_SEP + "fileFilter: " + getFileFilter()
+ LINE_SEP + "stackDeep: " + getStackDeep()
+ LINE_SEP + "stackOffset: " + getStackOffset()
+ LINE_SEP + "saveDays: " + getSaveDays()
- + LINE_SEP + "formatter: " + I_FORMATTER_MAP;
+ + LINE_SEP + "formatter: " + I_FORMATTER_MAP
+ + LINE_SEP + "fileWriter: " + mFileWriter
+ + LINE_SEP + "onConsoleOutputListener: " + mOnConsoleOutputListener
+ + LINE_SEP + "onFileOutputListener: " + mOnFileOutputListener
+ + LINE_SEP + "fileExtraHeader: " + mFileHead.getAppended();
}
}
@@ -842,6 +892,14 @@ public interface IFileWriter {
void write(String file, String content);
}
+ public interface OnConsoleOutputListener {
+ void onConsoleOutput(@TYPE int type, String tag, String content);
+ }
+
+ public interface OnFileOutputListener {
+ void onFileOutput(String filePath, String content);
+ }
+
private final static class TagHead {
String tag;
String[] consoleHead;
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/MessengerUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/MessengerUtils.java
index 9f16830ecc..5b246d7c47 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/MessengerUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/MessengerUtils.java
@@ -1,22 +1,29 @@
package com.blankj.utilcode.util;
import android.annotation.SuppressLint;
+import android.app.Notification;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
@@ -48,8 +55,7 @@ public static void register() {
Log.i("MessengerUtils", "Server service is running.");
return;
}
- Intent intent = new Intent(Utils.getApp(), ServerService.class);
- Utils.getApp().startService(intent);
+ startServiceCompat(new Intent(Utils.getApp(), ServerService.class));
return;
}
if (sLocalClient == null) {
@@ -92,12 +98,14 @@ public static void register(final String pkgName) {
}
public static void unregister(final String pkgName) {
- if (sClientMap.containsKey(pkgName)) {
- Client client = sClientMap.get(pkgName);
- sClientMap.remove(pkgName);
- client.unbind();
- } else {
+ if (!sClientMap.containsKey(pkgName)) {
Log.i("MessengerUtils", "unregister: client didn't register: " + pkgName);
+ return;
+ }
+ Client client = sClientMap.get(pkgName);
+ sClientMap.remove(pkgName);
+ if (client != null) {
+ client.unbind();
}
}
@@ -116,13 +124,26 @@ public static void post(@NonNull String key, @NonNull Bundle data) {
} else {
Intent intent = new Intent(Utils.getApp(), ServerService.class);
intent.putExtras(data);
- Utils.getApp().startService(intent);
+ startServiceCompat(intent);
}
for (Client client : sClientMap.values()) {
client.sendMsg2Server(data);
}
}
+ private static void startServiceCompat(Intent intent) {
+ try {
+ intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Utils.getApp().startForegroundService(intent);
+ } else {
+ Utils.getApp().startService(intent);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
static class Client {
String mPkgName;
@@ -133,13 +154,12 @@ static class Client {
@Override
public void handleMessage(Message msg) {
Bundle data = msg.getData();
- if (data != null) {
- String key = data.getString(KEY_STRING);
- if (key != null) {
- MessageCallback callback = subscribers.get(key);
- if (callback != null) {
- callback.messageCall(data);
- }
+ data.setClassLoader(MessengerUtils.class.getClassLoader());
+ String key = data.getString(KEY_STRING);
+ if (key != null) {
+ MessageCallback callback = subscribers.get(key);
+ if (callback != null) {
+ callback.messageCall(data);
}
}
}
@@ -153,11 +173,12 @@ public void onServiceConnected(ComponentName name, IBinder service) {
mServer = new Messenger(service);
int key = UtilsBridge.getCurrentProcessName().hashCode();
Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_SUBSCRIBE, key, 0);
+ msg.getData().setClassLoader(MessengerUtils.class.getClassLoader());
msg.replyTo = mClient;
try {
mServer.send(msg);
} catch (RemoteException e) {
- Log.e("MessengerUtils", "onServiceConnected: ", e);
+ e.printStackTrace();
}
sendCachedMsg2Server();
}
@@ -197,12 +218,13 @@ boolean bind() {
}
void unbind() {
- Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_UNSUBSCRIBE);
+ int key = UtilsBridge.getCurrentProcessName().hashCode();
+ Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_UNSUBSCRIBE, key, 0);
msg.replyTo = mClient;
try {
mServer.send(msg);
} catch (RemoteException e) {
- Log.e("MessengerUtils", "unbind: ", e);
+ e.printStackTrace();
}
try {
Utils.getApp().unbindService(mConn);
@@ -232,13 +254,14 @@ private void sendCachedMsg2Server() {
private boolean send2Server(Bundle bundle) {
Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_SEND);
+ bundle.setClassLoader(MessengerUtils.class.getClassLoader());
msg.setData(bundle);
msg.replyTo = mClient;
try {
mServer.send(msg);
return true;
} catch (RemoteException e) {
- Log.e("MessengerUtils", "send2Server: ", e);
+ e.printStackTrace();
return false;
}
}
@@ -279,6 +302,12 @@ public IBinder onBind(Intent intent) {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Notification notification = UtilsBridge.getNotification(
+ NotificationUtils.ChannelConfig.DEFAULT_CHANNEL_CONFIG, null
+ );
+ startForeground(1, notification);
+ }
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null) {
@@ -293,15 +322,17 @@ public int onStartCommand(Intent intent, int flags, int startId) {
}
private void sendMsg2Client(final Message msg) {
+ final Message obtain = Message.obtain(msg); //Copy the original
for (Messenger client : mClientMap.values()) {
try {
if (client != null) {
- client.send(msg);
+ client.send(Message.obtain(obtain));
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
+ obtain.recycle(); //Recycled copy
}
private void consumeServerProcessCallback(final Message msg) {
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java
index 6f6cf85b80..0ff8fa8798 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java
@@ -8,7 +8,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
/**
*
@@ -118,7 +118,7 @@ public static String getMetaDataInService(@NonNull final Class extends Service
*/
public static String getMetaDataInReceiver(@NonNull final BroadcastReceiver receiver,
@NonNull final String key) {
- return getMetaDataInReceiver(receiver, key);
+ return getMetaDataInReceiver(receiver.getClass(), key);
}
/**
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java
index 20145fe440..e73b6487db 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java
@@ -6,15 +6,18 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresPermission;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.Formatter;
-import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresPermission;
import java.lang.reflect.Method;
import java.net.InetAddress;
@@ -22,12 +25,18 @@
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CopyOnWriteArraySet;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.CHANGE_WIFI_STATE;
@@ -226,8 +235,8 @@ public static boolean isAvailableByDns(final String domain) {
return inetAddress != null;
} catch (UnknownHostException e) {
e.printStackTrace();
+ return false;
}
- return false;
}
/**
@@ -250,10 +259,38 @@ public static boolean getMobileDataEnabled() {
return (boolean) getMobileDataEnabledMethod.invoke(tm);
}
} catch (Exception e) {
- Log.e("NetworkUtils", "getMobileDataEnabled: ", e);
+ e.printStackTrace();
}
return false;
}
+
+ /**
+ * Returns true if device is connecting to the internet via a proxy, works for both Wi-Fi and Mobile Data.
+ *
+ * @return true if using proxy to connect to the internet.
+ */
+ public static boolean isBehindProxy(){
+ return !(System.getProperty("http.proxyHost") == null || System.getProperty("http.proxyPort") == null);
+ }
+
+ /**
+ * Returns true if device is connecting to the internet via a VPN.
+ *
+ * @return true if using VPN to conncet to the internet.
+ */
+ public static boolean isUsingVPN(){
+ ConnectivityManager cm = (ConnectivityManager) com.blankj.utilcode.util.Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ return cm.getNetworkInfo(ConnectivityManager.TYPE_VPN).isConnectedOrConnecting();
+ } else {
+ return cm.getNetworkInfo(NetworkCapabilities.TRANSPORT_VPN).isConnectedOrConnecting();
+ }
+ }
+
+
+
+
+
/**
* Return whether using mobile data.
@@ -660,11 +697,33 @@ public static String getServerAddressByWifi() {
return Formatter.formatIpAddress(wm.getDhcpInfo().serverAddress);
}
+ /**
+ * Return the ssid.
+ *
+ * @return the ssid.
+ */
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public static String getSSID() {
+ WifiManager wm = (WifiManager) Utils.getApp().getApplicationContext().getSystemService(WIFI_SERVICE);
+ if (wm == null) return "";
+ WifiInfo wi = wm.getConnectionInfo();
+ if (wi == null) return "";
+ String ssid = wi.getSSID();
+ if (TextUtils.isEmpty(ssid)) {
+ return "";
+ }
+ if (ssid.length() > 2 && ssid.charAt(0) == '"' && ssid.charAt(ssid.length() - 1) == '"') {
+ return ssid.substring(1, ssid.length() - 1);
+ }
+ return ssid;
+ }
+
/**
* Register the status of network changed listener.
*
* @param listener The status of network changed listener
*/
+ @RequiresPermission(ACCESS_NETWORK_STATE)
public static void registerNetworkStatusChangedListener(final OnNetworkStatusChangedListener listener) {
NetworkChangedReceiver.getInstance().registerListener(listener);
}
@@ -688,6 +747,123 @@ public static void unregisterNetworkStatusChangedListener(final OnNetworkStatusC
NetworkChangedReceiver.getInstance().unregisterListener(listener);
}
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, ACCESS_COARSE_LOCATION})
+ public static WifiScanResults getWifiScanResult() {
+ WifiScanResults result = new WifiScanResults();
+ if (!getWifiEnabled()) return result;
+ @SuppressLint("WifiManagerLeak")
+ WifiManager wm = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);
+ //noinspection ConstantConditions
+ List results = wm.getScanResults();
+ if (results != null) {
+ result.setAllResults(results);
+ }
+ return result;
+ }
+
+ private static final long SCAN_PERIOD_MILLIS = 3000;
+ private static final Set> SCAN_RESULT_CONSUMERS = new CopyOnWriteArraySet<>();
+ private static Timer sScanWifiTimer;
+ private static WifiScanResults sPreWifiScanResults;
+
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_COARSE_LOCATION})
+ public static void addOnWifiChangedConsumer(final Utils.Consumer consumer) {
+ if (consumer == null) return;
+ UtilsBridge.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (SCAN_RESULT_CONSUMERS.isEmpty()) {
+ SCAN_RESULT_CONSUMERS.add(consumer);
+ startScanWifi();
+ return;
+ }
+ consumer.accept(sPreWifiScanResults);
+ SCAN_RESULT_CONSUMERS.add(consumer);
+ }
+ });
+ }
+
+ private static void startScanWifi() {
+ sPreWifiScanResults = new WifiScanResults();
+ sScanWifiTimer = new Timer();
+ sScanWifiTimer.schedule(new TimerTask() {
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_COARSE_LOCATION})
+ @Override
+ public void run() {
+ startScanWifiIfEnabled();
+ WifiScanResults scanResults = getWifiScanResult();
+ if (isSameScanResults(sPreWifiScanResults.allResults, scanResults.allResults)) {
+ return;
+ }
+ sPreWifiScanResults = scanResults;
+ UtilsBridge.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ for (Utils.Consumer consumer : SCAN_RESULT_CONSUMERS) {
+ consumer.accept(sPreWifiScanResults);
+ }
+ }
+ });
+ }
+ }, 0, SCAN_PERIOD_MILLIS);
+ }
+
+ @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE})
+ private static void startScanWifiIfEnabled() {
+ if (!getWifiEnabled()) return;
+ @SuppressLint("WifiManagerLeak")
+ WifiManager wm = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);
+ //noinspection ConstantConditions
+ wm.startScan();
+ }
+
+ public static void removeOnWifiChangedConsumer(final Utils.Consumer consumer) {
+ if (consumer == null) return;
+ UtilsBridge.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ SCAN_RESULT_CONSUMERS.remove(consumer);
+ if (SCAN_RESULT_CONSUMERS.isEmpty()) {
+ stopScanWifi();
+ }
+ }
+ });
+ }
+
+ private static void stopScanWifi() {
+ if (sScanWifiTimer != null) {
+ sScanWifiTimer.cancel();
+ sScanWifiTimer = null;
+ }
+ }
+
+ private static boolean isSameScanResults(List l1, List l2) {
+ if (l1 == null && l2 == null) {
+ return true;
+ }
+ if (l1 == null || l2 == null) {
+ return false;
+ }
+ if (l1.size() != l2.size()) {
+ return false;
+ }
+ for (int i = 0; i < l1.size(); i++) {
+ ScanResult r1 = l1.get(i);
+ ScanResult r2 = l2.get(i);
+ if (!isSameScanResultContent(r1, r2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isSameScanResultContent(ScanResult r1, ScanResult r2) {
+ return r1 != null && r2 != null && UtilsBridge.equals(r1.BSSID, r2.BSSID)
+ && UtilsBridge.equals(r1.SSID, r2.SSID)
+ && UtilsBridge.equals(r1.capabilities, r2.capabilities)
+ && r1.level == r2.level;
+ }
+
public static final class NetworkChangedReceiver extends BroadcastReceiver {
private static NetworkChangedReceiver getInstance() {
@@ -697,11 +873,12 @@ private static NetworkChangedReceiver getInstance() {
private NetworkType mType;
private Set mListeners = new HashSet<>();
+ @RequiresPermission(ACCESS_NETWORK_STATE)
void registerListener(final OnNetworkStatusChangedListener listener) {
if (listener == null) return;
UtilsBridge.runOnUiThread(new Runnable() {
- @SuppressLint("MissingPermission")
@Override
+ @RequiresPermission(ACCESS_NETWORK_STATE)
public void run() {
int preSize = mListeners.size();
mListeners.add(listener);
@@ -733,13 +910,13 @@ public void run() {
});
}
- @SuppressLint("MissingPermission")
@Override
public void onReceive(Context context, Intent intent) {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
// debouncing
UtilsBridge.runOnUiThreadDelayed(new Runnable() {
@Override
+ @RequiresPermission(ACCESS_NETWORK_STATE)
public void run() {
NetworkType networkType = NetworkUtils.getNetworkType();
if (mType == networkType) return;
@@ -842,4 +1019,45 @@ public interface OnNetworkStatusChangedListener {
void onConnected(NetworkType networkType);
}
+
+ public static final class WifiScanResults {
+
+ private List allResults = new ArrayList<>();
+ private List filterResults = new ArrayList<>();
+
+ public WifiScanResults() {
+ }
+
+ public List getAllResults() {
+ return allResults;
+ }
+
+ public List getFilterResults() {
+ return filterResults;
+ }
+
+ public void setAllResults(List allResults) {
+ this.allResults = allResults;
+ filterResults = filterScanResult(allResults);
+ }
+
+ private static List filterScanResult(final List results) {
+ if (results == null || results.isEmpty()) {
+ return new ArrayList<>();
+ }
+ LinkedHashMap map = new LinkedHashMap<>(results.size());
+ for (ScanResult result : results) {
+ if (TextUtils.isEmpty(result.SSID)) {
+ continue;
+ }
+ ScanResult resultInMap = map.get(result.SSID);
+ if (resultInMap != null && resultInMap.level >= result.level) {
+ continue;
+ }
+ map.put(result.SSID, result);
+ }
+ return new ArrayList<>(map.values());
+ }
+
+ }
}
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java
index 42a97f5066..cea486d094 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java
@@ -8,15 +8,16 @@
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
-import android.support.annotation.IntDef;
-import android.support.annotation.RequiresPermission;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationManagerCompat;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
+import androidx.annotation.IntDef;
+import androidx.annotation.RequiresPermission;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
import static android.Manifest.permission.EXPAND_STATUS_BAR;
/**
@@ -91,21 +92,26 @@ public static void notify(int id, ChannelConfig channelConfig, Utils.Consumer consumer) {
+ NotificationManagerCompat.from(Utils.getApp()).notify(tag, id, getNotification(channelConfig, consumer));
+ }
+
+
+ public static Notification getNotification(ChannelConfig channelConfig, Utils.Consumer consumer) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) Utils.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
//noinspection ConstantConditions
nm.createNotificationChannel(channelConfig.getNotificationChannel());
}
- NotificationManagerCompat nmc = NotificationManagerCompat.from(Utils.getApp());
-
NotificationCompat.Builder builder = new NotificationCompat.Builder(Utils.getApp());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(channelConfig.mNotificationChannel.getId());
}
- consumer.accept(builder);
+ if (consumer != null) {
+ consumer.accept(builder);
+ }
- nmc.notify(tag, id, builder.build());
+ return builder.build();
}
/**
diff --git a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java
index d20a32574a..b835448861 100644
--- a/lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java
+++ b/lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java
@@ -1,10 +1,6 @@
package com.blankj.utilcode.util;
import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.RequiresApi;
-import android.support.v4.util.LongSparseArray;
-import android.support.v4.util.SimpleArrayMap;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -16,6 +12,11 @@
import java.util.Comparator;
import java.util.Map;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.collection.LongSparseArray;
+import androidx.collection.SimpleArrayMap;
+
/**
*