() }
28 | }
29 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/calendar/Functions.kt:
--------------------------------------------------------------------------------
1 |
2 | package com.xzy.utils.calendar
3 |
4 | import java.util.Calendar
5 |
6 | @Suppress("unused")
7 | object CalendarUtil {
8 |
9 | fun getCalendar(): String {
10 | val year = Calendar.getInstance().get(Calendar.YEAR)
11 | val month = Calendar.getInstance().get(Calendar.MONTH)
12 | val day = Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
13 | return (year.toString().substring(2, 4) + String.format("%02d", month + 1) +
14 | String.format("%02d", day))
15 | }
16 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/click/ClickUtils2.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.click;
2 |
3 | import android.util.Log;
4 | import android.view.MotionEvent;
5 | import android.view.View;
6 |
7 | import androidx.annotation.IntRange;
8 | import androidx.annotation.NonNull;
9 |
10 |
11 | /**
12 | * 参考:https://www.cnblogs.com/liupengfei005257/p/7448609.html
13 | * Android 之有效防止按钮多次重复点击的方法
14 | *
15 | * 调用:
16 | * if (!ButtonUtils.isFastDoubleClick(viewId)) {
17 | * //写你相关操作即可
18 | * }
19 | *
20 | * @author xzy
21 | */
22 | @SuppressWarnings("unused")
23 | public class ClickUtils2 {
24 | private static long lastClickTime = 0;
25 | private static long DIFF = 1000;
26 | private static int lastButtonId = -1;
27 |
28 | /**
29 | * 判断两次点击的间隔,如果小于1000,则认为是多次无效点击
30 | *
31 | * @return
32 | */
33 | public static boolean isFastDoubleClick() {
34 | return isFastDoubleClick(-1, DIFF);
35 | }
36 |
37 | /**
38 | * 判断两次点击的间隔,如果小于1000,则认为是多次无效点击
39 | *
40 | * @return
41 | */
42 | public static boolean isFastDoubleClick(int buttonId) {
43 | return isFastDoubleClick(buttonId, DIFF);
44 | }
45 |
46 | /**
47 | * 判断两次点击的间隔,如果小于diff,则认为是多次无效点击
48 | *
49 | * @param diff
50 | * @return
51 | */
52 | public static boolean isFastDoubleClick(int buttonId, long diff) {
53 | long time = System.currentTimeMillis();
54 | long timeD = time - lastClickTime;
55 | if (lastButtonId == buttonId && lastClickTime > 0 && timeD < diff) {
56 | Log.v("isFastDoubleClick", "短时间内按钮多次触发");
57 | return true;
58 | }
59 | lastClickTime = time;
60 | lastButtonId = buttonId;
61 | return false;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/clone/CloneUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.clone;
2 |
3 |
4 | import com.google.gson.Gson;
5 |
6 | import java.lang.reflect.Type;
7 |
8 | /**
9 | * 克隆工具类。
10 | * 参考 https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/
11 | * java/com/blankj/utilcode/util/CloneUtils.java
12 | *
13 | * @author xzy
14 | */
15 | @SuppressWarnings("unused")
16 | public final class CloneUtils {
17 |
18 | private CloneUtils() {
19 | throw new UnsupportedOperationException("u can't instantiate me...");
20 | }
21 |
22 | /**
23 | * Deep clone.
24 | *
25 | * @param data The data.
26 | * @param type The type.
27 | * @param The value type.
28 | * @return The object of cloned.
29 | */
30 | public static T deepClone(final T data, final Type type) {
31 | try {
32 | Gson gson = new Gson();
33 | return gson.fromJson(gson.toJson(data), type);
34 | } catch (Exception e) {
35 | e.printStackTrace();
36 | return null;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/constants/CacheConstants.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.constants;
2 |
3 |
4 | /**
5 | * cache 常量。
6 | *
7 | * @author xzy
8 | */
9 | @SuppressWarnings("unused")
10 | public interface CacheConstants {
11 | int SEC = 1;
12 | int MIN = 60;
13 | int HOUR = 3600;
14 | int DAY = 86400;
15 | }
16 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/constants/MemoryConstants.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.constants;
2 |
3 |
4 | import androidx.annotation.IntDef;
5 |
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 |
9 | /**
10 | * memory 常量。
11 | *
12 | * @author xzy
13 | */
14 | @SuppressWarnings("unused")
15 | public final class MemoryConstants {
16 |
17 | public static final int BYTE = 1;
18 | public static final int KB = 1024;
19 | public static final int MB = 1048576;
20 | public static final int GB = 1073741824;
21 |
22 | @IntDef({BYTE, KB, MB, GB})
23 | @Retention(RetentionPolicy.SOURCE)
24 | public @interface Unit {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/constants/TimeConstants.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.constants;
2 |
3 |
4 | import androidx.annotation.IntDef;
5 |
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 |
9 | /**
10 | * 时间 常量。
11 | *
12 | * @author xzy
13 | */
14 | @SuppressWarnings("unused")
15 | public final class TimeConstants {
16 |
17 | public static final int MSEC = 1;
18 | public static final int SEC = 1000;
19 | public static final int MIN = 60000;
20 | public static final int HOUR = 3600000;
21 | public static final int DAY = 86400000;
22 |
23 | @IntDef({MSEC, SEC, MIN, HOUR, DAY})
24 | @Retention(RetentionPolicy.SOURCE)
25 | public @interface Unit {
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/context/Activity.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.context
4 |
5 | import android.app.Activity
6 | import android.content.ActivityNotFoundException
7 | import android.content.Context
8 | import android.content.Intent
9 | import android.os.Bundle
10 |
11 | /**
12 | * Starts the Activity [targetActivity], in a more concise way, while still allowing to configure
13 | * the [Intent] in the optional [configIntent] lambda.
14 | */
15 | inline fun Context.startActivity(
16 | extras: Bundle? = null,
17 | configIntent: Intent.() -> Unit = {}
18 | ) {
19 | val intent = Intent(this, targetActivity::class.java)
20 | if (extras != null) {
21 | intent.putExtras(extras)
22 | }
23 | startActivity(intent.apply(configIntent))
24 | }
25 |
26 | /**
27 | * Starts the Activity that with the passed [packageName] and [activityName], in a more concise way,
28 | * while still allowing to configure the [Intent] in the optional [configIntent] lambda.
29 | *
30 | * If there's no matching [Activity], the underlying platform API will throw an
31 | * [ActivityNotFoundException].
32 | */
33 | @Throws(ActivityNotFoundException::class)
34 | inline fun Context.startActivity(
35 | packageName: String? = null,
36 | activityName: String,
37 | extras: Bundle? = null,
38 | singleTask: Boolean = false,
39 | configIntent: Intent.() -> Unit = {}
40 | ) {
41 | val intent = Intent()
42 | val pkgName = packageName ?: getPackageName()
43 | intent.setClassName(pkgName, activityName)
44 | if (extras != null) {
45 | intent.putExtras(extras)
46 | }
47 | if (singleTask) {
48 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
49 | }
50 | startActivity(intent.apply(configIntent))
51 | }
52 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/context/Intent.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.context
4 |
5 | import android.content.ComponentName
6 | import android.content.Intent
7 | import com.xzy.utils.appctx.appCtx
8 |
9 | /***
10 | * Android L (lollipop, API 21) introduced a new problem when trying to invoke implicit intent,
11 | * "java.lang.IllegalArgumentException: Service Intent must be explicit"
12 | *
13 | * If you are using an implicit intent, and know only 1 target would answer this intent,
14 | * This method will help you turn the implicit intent into the explicit form.
15 | */
16 | fun Intent.toExplicitServiceIntent(): Intent? {
17 | // Retrieve all services that can match the given intent
18 | val pm = appCtx.packageManager
19 | val resolveInfos = pm.queryIntentServices(this, 0)
20 |
21 | if (resolveInfos.size != 1) {
22 | return null
23 | }
24 |
25 | // Get component info and create ComponentName
26 | val resolveInfo = resolveInfos[0]
27 | val packageName = resolveInfo.serviceInfo.packageName
28 | val className = resolveInfo.serviceInfo.name
29 | val component = ComponentName(packageName, className)
30 |
31 | // Create a new intent. Use the old one for extras and such reuse
32 | val explicitIntent = Intent(this)
33 | explicitIntent.component = component
34 |
35 | return explicitIntent
36 | }
37 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/countdown/CountdownUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.countdown;
2 |
3 | import android.os.CountDownTimer;
4 |
5 | /**
6 | * 倒计时工具类
7 | * 参考 https://github.com/albert-lii/SUtils/blob/master/sutils/src/main
8 | * /java/com/liyi/sutils/utils/time/CountdownUtil.java
9 | * @author xzy
10 | */
11 | @SuppressWarnings("unused")
12 | public class CountdownUtils {
13 | /* 倒计时的间隔时间 */
14 | private int mIntervalTime;
15 | /* 倒计时的总时间 */
16 | private int mTotalTime;
17 | /* 倒计时是否在运行 */
18 | private boolean isRunning;
19 | /* 系统倒计时类 */
20 | private CountDownTimer mTimer;
21 | /* 倒计时监听器 */
22 | private OnCountdownListener mCountdownListener;
23 |
24 | private CountdownUtils() {
25 | super();
26 | this.mIntervalTime = 0;
27 | this.mTotalTime = 0;
28 | this.isRunning = false;
29 | }
30 |
31 | public static CountdownUtils newInstance() {
32 | return new CountdownUtils();
33 | }
34 |
35 | private void init() {
36 | mTimer = new CountDownTimer(mTotalTime, mIntervalTime) {
37 | @Override
38 | public void onTick(long millisUntilFinished) {
39 | if (mCountdownListener != null) {
40 | mCountdownListener.onRemain(millisUntilFinished);
41 | }
42 | }
43 |
44 | @Override
45 | public void onFinish() {
46 | isRunning = false;
47 | if (mCountdownListener != null) {
48 | mCountdownListener.onFinish();
49 | }
50 | }
51 | };
52 | }
53 |
54 | /**
55 | * 设置倒计时的间隔时间
56 | *
57 | * @param intervalTime 间隔时间
58 | * @return {@link CountdownUtils}
59 | */
60 | public CountdownUtils intervalTime(int intervalTime) {
61 | this.mIntervalTime = intervalTime;
62 | return this;
63 | }
64 |
65 | /**
66 | * 设置倒计时的总时间
67 | *
68 | * @param totalTime 总时间
69 | * @return {@link CountdownUtils}
70 | */
71 | public CountdownUtils totalTime(int totalTime) {
72 | this.mTotalTime = totalTime;
73 | return this;
74 | }
75 |
76 | /**
77 | * 设置倒计时监听器
78 | *
79 | * @param listener 倒计时监听器
80 | * @return {@link CountdownUtils}
81 | */
82 | public CountdownUtils callback(OnCountdownListener listener) {
83 | this.mCountdownListener = listener;
84 | return this;
85 | }
86 |
87 | /**
88 | * 开始倒计时
89 | */
90 | public void start() {
91 | if (mTimer == null) init();
92 | mTimer.start();
93 | isRunning = true;
94 | }
95 |
96 | /**
97 | * 结束倒计时
98 | */
99 | public void stop() {
100 | if (mTimer != null) mTimer.cancel();
101 | isRunning = false;
102 | }
103 |
104 | /**
105 | * 获取倒计时的间隔时间
106 | *
107 | * @return 间隔时间
108 | */
109 | public int getIntervalTime() {
110 | return mIntervalTime;
111 | }
112 |
113 | /**
114 | * 获取倒计时的总时间
115 | *
116 | * @return 总时间
117 | */
118 | public int getTotalTime() {
119 | return mTotalTime;
120 | }
121 |
122 | /**
123 | * 获取倒计时是否正在进行
124 | *
125 | * @return {@code true}: 正在进行
{@code false}: 已经结束
126 | */
127 | public boolean isRunning() {
128 | return isRunning;
129 | }
130 |
131 | /**
132 | * 获取倒计时类
133 | *
134 | * @return {@link CountDownTimer}
135 | */
136 | public CountDownTimer getTimer() {
137 | return mTimer;
138 | }
139 |
140 | /**
141 | * 倒计时监听器
142 | */
143 | public interface OnCountdownListener {
144 | /**
145 | * 倒计时正在进行时调用的方法
146 | *
147 | * @param millisUntilFinished 剩余的时间(毫秒)
148 | */
149 | void onRemain(long millisUntilFinished);
150 |
151 | /**
152 | * 倒计时结束
153 | */
154 | void onFinish();
155 | }
156 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/crypto/Crypto.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.crypto
4 |
5 | import android.util.Base64
6 | import android.util.Base64.NO_WRAP
7 | import com.xzy.utils.Const.DIGITS_LOWER
8 | import com.xzy.utils.Const.DIGITS_UPPER
9 | import java.io.BufferedInputStream
10 | import java.io.File
11 | import java.io.FileInputStream
12 | import java.security.MessageDigest
13 | import javax.crypto.Cipher
14 | import javax.crypto.spec.IvParameterSpec
15 | import javax.crypto.spec.SecretKeySpec
16 |
17 | /**
18 | * Converts an array of bytes into an array of characters representing
19 | * the hexadecimal values of each byte in order. The returned array will
20 | * be double the length of the passed array, as it takes two characters
21 | * to represent any given byte.
22 | *
23 | * @param toLowerCase `true` converts to lowercase, `false` to uppercase
24 | */
25 | fun ByteArray.encodeHex(toLowerCase: Boolean = true): CharArray {
26 | val l = size
27 | val out = CharArray(l shl 1)
28 |
29 | val toDigits = if (toLowerCase) DIGITS_LOWER else DIGITS_UPPER
30 |
31 | var i = 0
32 | var j = 0
33 | while (i < l) {
34 | out[j++] = toDigits[(0xF0 and get(i).toInt()).ushr(4)]
35 | out[j++] = toDigits[0x0F and get(i).toInt()]
36 | i++
37 | }
38 | return out
39 | }
40 |
41 | /**
42 | * Calculate string's md5 value, 32 chars.
43 | */
44 | fun String.md5(toLowerCase: Boolean = true): String {
45 | val digest = MessageDigest.getInstance("MD5")
46 | digest.update(toByteArray())
47 | val bytes = digest.digest()
48 | return String(bytes.encodeHex(toLowerCase))
49 | }
50 |
51 | /**
52 | * Calculate string's short md5 value, 16 chars.
53 | */
54 | fun String.md5Short(toLowerCase: Boolean = true): String {
55 | return md5(toLowerCase).substring(8, 24)
56 | }
57 |
58 | /**
59 | * Calculate file's md5 value, 32 chars.
60 | */
61 | fun File.md5(toLowerCase: Boolean = true): String {
62 | val bis = BufferedInputStream(FileInputStream(this))
63 | val digest = MessageDigest.getInstance("MD5")
64 | val bytes = digest.digest(bis.readBytes())
65 | return String(bytes.encodeHex(toLowerCase))
66 | }
67 |
68 | /**
69 | * Calculate file's short md5 value, 16 chars.
70 | */
71 | fun File.md5Short(toLowerCase: Boolean = true): String {
72 | return md5(toLowerCase).substring(8, 24)
73 | }
74 |
75 | /**
76 | * Encode string by base64, NO_WRAP mode.
77 | */
78 | fun String.encodeBase64(): String {
79 | return Base64.encodeToString(toByteArray(), NO_WRAP)
80 | }
81 |
82 | /**
83 | * Decode string by base64, NO_WRAP mode.
84 | */
85 | fun String.decodeBase64(): String {
86 | return String(Base64.decode(this, NO_WRAP))
87 | }
88 |
89 | /**
90 | * Encode string by aes.
91 | */
92 | fun String.encodeAes(password: String): String {
93 | val secretKeySpec = SecretKeySpec(password.toByteArray(), "AES")
94 | val iv = ByteArray(16)
95 | val charArray = password.toCharArray()
96 | for (i in charArray.indices) {
97 | iv[i] = charArray[i].toByte()
98 | }
99 | val ivParameterSpec = IvParameterSpec(iv)
100 |
101 | val cipher = Cipher.getInstance("AES/GCM/NoPadding")
102 | cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec)
103 |
104 | val encryptedValue = cipher.doFinal(this.toByteArray())
105 | return Base64.encodeToString(encryptedValue, Base64.DEFAULT)
106 | }
107 |
108 | /**
109 | * Decode string by aes.
110 | */
111 | fun String.decodeAes(password: String): String {
112 | val secretKeySpec = SecretKeySpec(password.toByteArray(), "AES")
113 | val iv = ByteArray(16)
114 | val charArray = password.toCharArray()
115 | for (i in charArray.indices) {
116 | iv[i] = charArray[i].toByte()
117 | }
118 | val ivParameterSpec = IvParameterSpec(iv)
119 |
120 | val cipher = Cipher.getInstance("AES/GCM/NoPadding")
121 | cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec)
122 |
123 | val decryptedByteValue = cipher.doFinal(Base64.decode(this, NO_WRAP))
124 | return String(decryptedByteValue)
125 | }
126 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/crypto/Serialization.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.crypto
4 |
5 | import java.io.ByteArrayInputStream
6 | import java.io.ByteArrayOutputStream
7 | import java.io.ObjectInputStream
8 | import java.io.ObjectOutputStream
9 | import java.io.Serializable
10 |
11 | fun Serializable.serialize(): ByteArray {
12 | val baos = ByteArrayOutputStream(512)
13 | val oos = ObjectOutputStream(baos)
14 | oos.use {
15 | oos.writeObject(this)
16 | }
17 | return baos.toByteArray()
18 | }
19 |
20 | @Suppress("UNCHECKED_CAST")
21 | fun ByteArray.deserialize(): T {
22 | val bais = ByteArrayInputStream(this)
23 | val ois = ObjectInputStream(bais)
24 | return ois.readObject() as T
25 | }
26 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/density/DensityUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.density;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 | import android.util.TypedValue;
6 | import android.view.WindowManager;
7 |
8 | import java.util.Objects;
9 |
10 | /**
11 | * 屏幕分辨率辅助类。
12 | */
13 | @SuppressWarnings("unused")
14 | public class DensityUtils {
15 |
16 | private DensityUtils() {
17 | /* cannot be instantiated */
18 | throw new UnsupportedOperationException("cannot be instantiated");
19 | }
20 |
21 |
22 | /**
23 | * @param context 上下文
24 | * @return DisplayMetrics对象
25 | */
26 | public static DisplayMetrics getDisplayMetrics(Context context) {
27 | WindowManager windowManager = (WindowManager) context
28 | .getSystemService(Context.WINDOW_SERVICE);
29 | DisplayMetrics metrics = new DisplayMetrics();
30 | Objects.requireNonNull(windowManager).getDefaultDisplay().getMetrics(metrics);
31 | return metrics;
32 | }
33 |
34 | /**
35 | * 获取屏幕分辨率-宽
36 | *
37 | * @param context 上下文
38 | * @return 宽
39 | */
40 | public static int getScreenWidth(Context context) {
41 | DisplayMetrics metrics = getDisplayMetrics(context);
42 | return metrics.widthPixels;
43 | }
44 |
45 | /**
46 | * 获取屏幕分辨率-高
47 | *
48 | * @param context 上下文
49 | * @return 高
50 | */
51 | public static int getScreenHeight(Context context) {
52 | DisplayMetrics metrics = getDisplayMetrics(context);
53 | return metrics.heightPixels;
54 | }
55 |
56 | /**
57 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
58 | *
59 | * @param context 上下文
60 | * @param dpValue 值
61 | * @return 转换结果
62 | */
63 | public static int dip2px(Context context, float dpValue) {
64 | final float scale = context.getResources().getDisplayMetrics().density;
65 | return (int) (dpValue * scale + 0.5f);
66 | }
67 |
68 | /**
69 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
70 | *
71 | * @param context 上下文
72 | * @param pxValue 值
73 | * @return 转换结果
74 | */
75 | public static int px2dip(Context context, float pxValue) {
76 | final float scale = context.getResources().getDisplayMetrics().density;
77 | return (int) (pxValue / scale + 0.5f);
78 | }
79 |
80 | /**
81 | * dp 转 px.
82 | */
83 | public static int dp2px(Context context, float dp) {
84 | //获取设备密度
85 | float density = context.getResources().getDisplayMetrics().density;
86 | //4.3, 4.9, 加0.5是为了四舍五入
87 | return (int) (dp * density + 0.5f);
88 | }
89 |
90 | /**
91 | * px 转 dp.
92 | */
93 | public static float px2dp(Context context, int px) {
94 | //获取设备密度
95 | float density = context.getResources().getDisplayMetrics().density;
96 | return px / density;
97 | }
98 |
99 | /**
100 | * sp 转 px
101 | *
102 | * @param context context
103 | * @param spValue spValue
104 | * @return int
105 | */
106 | public static int sp2px(Context context, float spValue) {
107 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
108 | spValue, context.getResources().getDisplayMetrics());
109 | }
110 |
111 | /**
112 | * px 转 dp
113 | *
114 | * @param context context
115 | * @param pxValue pxValue
116 | * @return float
117 | */
118 | public static float px2dp(Context context, float pxValue) {
119 | final float scale = context.getResources().getDisplayMetrics().density;
120 | return (pxValue / scale);
121 | }
122 |
123 |
124 | /**
125 | * px 转 sp
126 | *
127 | * @param context context
128 | * @param pxValue pxValue
129 | * @return float
130 | */
131 | public static float px2sp(Context context, float pxValue) {
132 | return (pxValue / context.getResources().getDisplayMetrics().scaledDensity);
133 | }
134 |
135 | }
136 |
137 |
138 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/dimension/Dimensions.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE", "unused")
2 |
3 | package com.xzy.utils.dimension
4 |
5 | import android.content.Context
6 | import android.view.View
7 |
8 | inline fun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt()
9 | inline fun Context.dp(value: Int): Float = (value * resources.displayMetrics.density)
10 |
11 | inline fun View.dip(value: Int) = context.dip(value)
12 | inline fun View.dp(value: Int) = context.dp(value)
13 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/exception/Exception.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.exception
4 |
5 | import android.util.Log
6 |
7 | /** Throws an [IllegalStateException] with a message that includes [value]. */
8 | fun unexpectedValue(value: Any?): Nothing = throw IllegalStateException("Unexpected value: $value.")
9 |
10 | /** Throws an [IllegalArgumentException] with the passed [argument] and [errorMsg]. */
11 | fun illegalArg(argument: Any?, errorMsg: String? = null): Nothing =
12 | throw IllegalArgumentException(
13 | "Illegal argument: $argument${if (errorMsg == null) "." else ", $errorMsg."}"
14 | )
15 |
16 | /** Throws an [UnsupportedOperationException] with the given message. */
17 | fun unsupported(errorMsg: String? = null): Nothing = throw UnsupportedOperationException(errorMsg)
18 |
19 | /** Get non null message from throwable. */
20 | val Throwable.msg: String
21 | get() = message ?: javaClass.simpleName
22 |
23 | /** Get stack trace message from throwable. */
24 | val Throwable.stackTraceMsg: String
25 | get() = Log.getStackTraceString(this)
26 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/file/bean/AppInfo.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.file.bean;
2 |
3 | import android.content.pm.ApplicationInfo;
4 | import android.graphics.drawable.Drawable;
5 |
6 | /**
7 | * @author xzy
8 | */
9 | @SuppressWarnings("ALL")
10 | public class AppInfo {
11 |
12 | private ApplicationInfo applicationInfo;
13 | private int versionCode = 0;
14 | /**
15 | * 图片的icon
16 | */
17 | private Drawable icon;
18 |
19 | /**
20 | * 程序的名字
21 | */
22 | private String apkName;
23 |
24 | /**
25 | * 程序大小
26 | */
27 | private long apkSize;
28 |
29 | /**
30 | * 表示到底是用户app还是系统app
31 | * 如果表示为true 就是用户app
32 | * 如果是false表示系统app
33 | */
34 | private boolean isUserApp;
35 |
36 | /**
37 | * 放置的位置
38 | */
39 | private boolean isRom;
40 |
41 | /**
42 | * 包名
43 | */
44 | private String apkPackageName;
45 |
46 | public Drawable getIcon() {
47 | return icon;
48 | }
49 |
50 | public void setIcon(Drawable icon) {
51 | this.icon = icon;
52 | }
53 |
54 | public String getApkName() {
55 | return apkName;
56 | }
57 |
58 | public void setApkName(String apkName) {
59 | this.apkName = apkName;
60 | }
61 |
62 | public long getApkSize() {
63 | return apkSize;
64 | }
65 |
66 | public void setApkSize(long apkSize) {
67 | this.apkSize = apkSize;
68 | }
69 |
70 | public boolean isUserApp() {
71 | return isUserApp;
72 | }
73 |
74 | public void setIsUserApp(boolean isUserApp) {
75 | this.isUserApp = isUserApp;
76 | }
77 |
78 | public boolean isRom() {
79 | return isRom;
80 | }
81 |
82 | public void setIsRom(boolean isRom) {
83 | this.isRom = isRom;
84 | }
85 |
86 | public String getApkPackageName() {
87 | return apkPackageName;
88 | }
89 |
90 | public void setApkPackageName(String apkPackageName) {
91 | this.apkPackageName = apkPackageName;
92 | }
93 |
94 | public ApplicationInfo getApplicationInfo() {
95 | return applicationInfo;
96 | }
97 |
98 | public void setApplicationInfo(ApplicationInfo applicationInfo) {
99 | this.applicationInfo = applicationInfo;
100 | }
101 |
102 | public int getVersionCode() {
103 | return versionCode;
104 | }
105 |
106 | public void setVersionCode(int versionCode) {
107 | this.versionCode = versionCode;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/file/bean/FileBean.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.file.bean;
2 |
3 | /**
4 | * 文件,可以是文档、apk、压缩包
5 | *
6 | * @author xzy
7 | */
8 | @SuppressWarnings("ALL")
9 | public class FileBean {
10 | /**
11 | * 文件的路径
12 | */
13 | public String path;
14 | /**
15 | * 文件图片资源的id,drawable或mipmap文件中已经存放doc、xml、xls等文件的图片
16 | */
17 | public int iconId;
18 |
19 | public FileBean(String path, int iconId) {
20 | this.path = path;
21 | this.iconId = iconId;
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | return "FileBean{" +
27 | "path='" + path + '\'' +
28 | ", iconId=" + iconId +
29 | '}';
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/file/bean/FileItem.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.file.bean;
2 |
3 | /**
4 | * 文件的Bean(文件管理/浏览模块中的一个item)
5 | *
6 | * @author xzy
7 | */
8 | @SuppressWarnings("all")
9 | public class FileItem {
10 | public int filePic;
11 | public String fileName;
12 | public String filePath;
13 | public String fileModifiedTime;
14 |
15 | public int getFilePic() {
16 | return filePic;
17 | }
18 |
19 | public void setFilePic(int filePic) {
20 | this.filePic = filePic;
21 | }
22 |
23 | public String getFileName() {
24 | return fileName;
25 | }
26 |
27 | public void setFileName(String fileName) {
28 | this.fileName = fileName;
29 | }
30 |
31 | public String getFilePath() {
32 | return filePath;
33 | }
34 |
35 | public void setFilePath(String filePath) {
36 | this.filePath = filePath;
37 | }
38 |
39 | public String getFileModifiedTime() {
40 | return fileModifiedTime;
41 | }
42 |
43 | public void setFileModifiedTime(String fileModifiedTime) {
44 | this.fileModifiedTime = fileModifiedTime;
45 | }
46 |
47 | public FileItem(int filePic, String fileName, String filePath,
48 | String fileModifiedTime) {
49 | super();
50 | this.filePic = filePic;
51 | this.fileName = fileName;
52 | this.filePath = filePath;
53 | this.fileModifiedTime = fileModifiedTime;
54 | }
55 |
56 | @Override
57 | public String toString() {
58 | return "FileItem [filePic=" + filePic + ", fileName=" + fileName
59 | + ", filePath=" + filePath + ", fileModifiedTime="
60 | + fileModifiedTime + "]";
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/file/bean/ImgFolderBean.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.file.bean;
2 |
3 | /**
4 | * 片文件夹的bean类
5 | *
6 | * @author xzy
7 | */
8 | @SuppressWarnings("all")
9 | public class ImgFolderBean {
10 | /**
11 | * 当前文件夹的路径
12 | */
13 | private String dir;
14 | /**
15 | * 第一张图片的路径
16 | */
17 | private String fistImgPath;
18 | /**
19 | * 文件夹名
20 | */
21 | private String name;
22 | /**
23 | * 文件夹中图片的数量
24 | */
25 | private int count;
26 |
27 | public String getDir() {
28 | return dir;
29 | }
30 |
31 | public void setDir(String dir) {
32 | this.dir = dir;
33 | int lastIndex = dir.lastIndexOf("/");
34 | this.name = dir.substring(lastIndex + 1);
35 | }
36 |
37 | public String getFistImgPath() {
38 | return fistImgPath;
39 | }
40 |
41 | public void setFistImgPath(String fistImgPath) {
42 | this.fistImgPath = fistImgPath;
43 | }
44 |
45 | public String getName() {
46 | return name;
47 | }
48 |
49 | public int getCount() {
50 | return count;
51 | }
52 |
53 | public void setCount(int count) {
54 | this.count = count;
55 | }
56 |
57 | public ImgFolderBean() {
58 | }
59 |
60 | public ImgFolderBean(String dir, String fistImgPath, String name, int count) {
61 | this.dir = dir;
62 | this.fistImgPath = fistImgPath;
63 | this.name = name;
64 | this.count = count;
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return "ImgFolderBean{" +
70 | "dir='" + dir + '\'' +
71 | ", fistImgPath='" + fistImgPath + '\'' +
72 | ", name='" + name + '\'' +
73 | ", count=" + count +
74 | '}';
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/file/bean/Music.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.file.bean;
2 |
3 | import com.xzy.utils.pinyin.PinyinUtils;
4 |
5 | /**
6 | * 音乐 bean
7 | *
8 | * @author xzy
9 | */
10 | @SuppressWarnings("all")
11 | public class Music implements Comparable {
12 | /**
13 | * 歌曲名
14 | */
15 | private String name;
16 | /**
17 | * 路径
18 | */
19 | private String path;
20 | /**
21 | * 所属专辑
22 | */
23 | private String album;
24 | /**
25 | * 艺术家(作者)
26 | */
27 | private String artist;
28 | /**
29 | * 文件大小
30 | */
31 | private long size;
32 | /**
33 | * 时长
34 | */
35 | private int duration;
36 |
37 | private String pinyin;
38 |
39 | public Music(String name, String path, String album,
40 | String artist, long size, int duration) {
41 | this.name = name;
42 | this.path = path;
43 | this.album = album;
44 | this.artist = artist;
45 | this.size = size;
46 | this.duration = duration;
47 | pinyin = PinyinUtils.getPinyin(name);
48 | }
49 |
50 | public String getName() {
51 | return name;
52 | }
53 |
54 | public void setName(String name) {
55 | this.name = name;
56 | }
57 |
58 | public String getPath() {
59 | return path;
60 | }
61 |
62 | public void setPath(String path) {
63 | this.path = path;
64 | }
65 |
66 | public String getAlbum() {
67 | return album;
68 | }
69 |
70 | public void setAlbum(String album) {
71 | this.album = album;
72 | }
73 |
74 | public String getArtist() {
75 | return artist;
76 | }
77 |
78 | public void setArtist(String artist) {
79 | this.artist = artist;
80 | }
81 |
82 | public long getSize() {
83 | return size;
84 | }
85 |
86 | public void setSize(long size) {
87 | this.size = size;
88 | }
89 |
90 | public int getDuration() {
91 | return duration;
92 | }
93 |
94 | public void setDuration(int duration) {
95 | this.duration = duration;
96 | }
97 |
98 | public String getPinyin() {
99 | return pinyin;
100 | }
101 |
102 | public void setPinyin(String pinyin) {
103 | this.pinyin = pinyin;
104 | }
105 |
106 | @Override
107 | public int compareTo(Music music) {
108 | return this.pinyin.compareTo(music.getPinyin());
109 | }
110 |
111 | @Override
112 | public String toString() {
113 | return "Music{" +
114 | "name='" + name + '\'' +
115 | ", path='" + path + '\'' +
116 | ", album='" + album + '\'' +
117 | ", artist='" + artist + '\'' +
118 | ", size=" + size +
119 | ", duration=" + duration +
120 | ", pinyin='" + pinyin + '\'' +
121 | '}';
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/file/bean/Video.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.file.bean;
2 |
3 | /**
4 | * 视频 bean
5 | *
6 | * @author xzy
7 | */
8 | @SuppressWarnings("all")
9 | public class Video {
10 | private int id = 0;
11 | private String path = null;
12 | private String name = null;
13 | private String resolution = null;// 分辨率
14 | private long size = 0;
15 | private long date = 0;
16 | private long duration = 0;
17 |
18 | public Video(int id, String path, String name, String resolution,
19 | long size, long date, long duration) {
20 | this.id = id;
21 | this.path = path;
22 | this.name = name;
23 | this.resolution = resolution;
24 | this.size = size;
25 | this.date = date;
26 | this.duration = duration;
27 | }
28 |
29 | public Video() {
30 | }
31 |
32 | public int getId() {
33 | return id;
34 | }
35 |
36 | public void setId(int id) {
37 | this.id = id;
38 | }
39 |
40 | public String getResolution() {
41 | return resolution;
42 | }
43 |
44 | public void setResolution(String resolution) {
45 | this.resolution = resolution;
46 | }
47 |
48 | public String getPath() {
49 | return path;
50 | }
51 |
52 | public void setPath(String path) {
53 | this.path = path;
54 | }
55 |
56 | public String getName() {
57 | return name;
58 | }
59 |
60 | public void setName(String name) {
61 | this.name = name;
62 | }
63 |
64 | public long getSize() {
65 | return size;
66 | }
67 |
68 | public void setSize(long size) {
69 | this.size = size;
70 | }
71 |
72 | public long getDate() {
73 | return date;
74 | }
75 |
76 | public void setDate(long date) {
77 | this.date = date;
78 | }
79 |
80 | public long getDuration() {
81 | return duration;
82 | }
83 |
84 | public void setDuration(long duration) {
85 | this.duration = duration;
86 | }
87 |
88 | @Override
89 | public String toString() {
90 | return "Video [id=" + id + ", path=" + path + ", name=" + name + "," +
91 | " resolution=" + resolution + ", size=" + size + ", date=" + date
92 | + ", duration=" + duration + "]";
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/flashlight/FlashlightUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.flashlight;
2 |
3 | import android.content.pm.PackageManager;
4 | import android.graphics.SurfaceTexture;
5 | import android.hardware.Camera;
6 | import android.util.Log;
7 |
8 | import com.xzy.utils.common.Utils;
9 |
10 | import java.io.IOException;
11 |
12 | import static android.hardware.Camera.Parameters.FLASH_MODE_OFF;
13 | import static android.hardware.Camera.Parameters.FLASH_MODE_TORCH;
14 |
15 | /**
16 | * 闪光灯相关的工具类。
17 | * 参考 https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/
18 | * java/com/blankj/utilcode/util/FlashlightUtils.java
19 | *
20 | * @author xzy
21 | */
22 | @SuppressWarnings("unused")
23 | public final class FlashlightUtils {
24 |
25 | private static Camera mCamera;
26 | private static SurfaceTexture mSurfaceTexture;
27 |
28 | private FlashlightUtils() {
29 | throw new UnsupportedOperationException("u can't instantiate me...");
30 | }
31 |
32 | /**
33 | * Return whether the device supports flashlight.
34 | *
35 | * @return {@code true}: yes
{@code false}: no
36 | */
37 | public static boolean isFlashlightEnable() {
38 | return Utils.getApp()
39 | .getPackageManager()
40 | .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
41 | }
42 |
43 | /**
44 | * Return whether the flashlight is working.
45 | *
46 | * @return {@code true}: yes
{@code false}: no
47 | */
48 | public static boolean isFlashlightOn() {
49 | if (!init()) return false;
50 | Camera.Parameters parameters = mCamera.getParameters();
51 | return FLASH_MODE_TORCH.equals(parameters.getFlashMode());
52 | }
53 |
54 | /**
55 | * Turn on or turn off the flashlight.
56 | *
57 | * @param isOn True to turn on the flashlight, false otherwise.
58 | */
59 | public static void setFlashlightStatus(final boolean isOn) {
60 | if (!init()) return;
61 | final Camera.Parameters parameters = mCamera.getParameters();
62 | if (isOn) {
63 | if (!FLASH_MODE_TORCH.equals(parameters.getFlashMode())) {
64 | try {
65 | mCamera.setPreviewTexture(mSurfaceTexture);
66 | mCamera.startPreview();
67 | parameters.setFlashMode(FLASH_MODE_TORCH);
68 | mCamera.setParameters(parameters);
69 | } catch (IOException e) {
70 | Log.e("FlashlightUtils", "setFlashlightStatusOn: ", e);
71 | }
72 | }
73 | } else {
74 | if (!FLASH_MODE_OFF.equals(parameters.getFlashMode())) {
75 | parameters.setFlashMode(FLASH_MODE_OFF);
76 | mCamera.setParameters(parameters);
77 | }
78 | }
79 | }
80 |
81 | /**
82 | * Destroy the flashlight.
83 | */
84 | public static void destroy() {
85 | if (mCamera == null) return;
86 | mCamera.release();
87 | mSurfaceTexture = null;
88 | mCamera = null;
89 | }
90 |
91 | private static boolean init() {
92 | if (mCamera == null) {
93 | try {
94 | mCamera = Camera.open(0);
95 | mSurfaceTexture = new SurfaceTexture(0);
96 | } catch (Throwable t) {
97 | Log.e("FlashlightUtils", "init failed: ", t);
98 | return false;
99 | }
100 | }
101 | if (mCamera == null) {
102 | Log.e("FlashlightUtils", "init failed.");
103 | return false;
104 | }
105 | return true;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/http/net/HttpGetUtil.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.http.net;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.io.UnsupportedEncodingException;
8 | import java.net.HttpURLConnection;
9 | import java.net.ProtocolException;
10 | import java.net.URL;
11 | import java.net.URLEncoder;
12 | import java.util.Map;
13 | import java.util.Objects;
14 |
15 | /**
16 | * get 请求工具类封装。使用的是标准的 java 接口:java.net
17 | *
18 | * @author xzy
19 | */
20 | @SuppressWarnings("unused")
21 | class HttpGetUtil {
22 | /**
23 | * 供外部调用的 get 请求。
24 | *
25 | * @param path 请求 url
26 | * @param params 请求参数 map 类型
27 | */
28 | static void getRequest(String path, Map params,
29 | HttpCallBackListener httpCallBackListener) {
30 | StringBuilder sb = new StringBuilder(path);
31 | if (params != null && !params.isEmpty()) {
32 | sb.append("?");
33 | for (Map.Entry entry : params.entrySet()) {
34 | sb.append(entry.getKey()).append("=");
35 | try {
36 | sb.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
37 | } catch (UnsupportedEncodingException e) {
38 | if (httpCallBackListener != null) {
39 | httpCallBackListener.onError(e);
40 | }
41 | }
42 | sb.append("&");
43 | }
44 | sb.deleteCharAt(sb.length() - 1);
45 | }
46 | HttpURLConnection conn = null;
47 | try {
48 | conn = (HttpURLConnection) new URL(sb.toString()).openConnection();
49 | } catch (IOException e) {
50 | if (httpCallBackListener != null) {
51 | httpCallBackListener.onError(e);
52 | }
53 | }
54 | Objects.requireNonNull(conn).setConnectTimeout(5000);
55 | try {
56 | conn.setRequestMethod("GET");
57 | } catch (ProtocolException e) {
58 | if (httpCallBackListener != null) {
59 | httpCallBackListener.onError(e);
60 | }
61 | }
62 | try {
63 | if (conn.getResponseCode() == 200) {
64 | InputStream in = conn.getInputStream();
65 | BufferedReader bufferReader = new BufferedReader(new InputStreamReader(in));
66 | String line;
67 | StringBuilder response = new StringBuilder();
68 | while ((line = bufferReader.readLine()) != null) {
69 | response.append(line);
70 | }
71 | if (httpCallBackListener != null) {
72 | httpCallBackListener.onFinish(response.toString());
73 | }
74 | }
75 | } catch (IOException e) {
76 | if (httpCallBackListener != null) {
77 | httpCallBackListener.onError(e);
78 | }
79 | } finally {
80 | conn.disconnect();
81 | }
82 | }
83 |
84 |
85 | /**
86 | * 回调接口。
87 | */
88 | interface HttpCallBackListener {
89 |
90 | /**
91 | * 回调成功。
92 | *
93 | * @param response 成功响应
94 | */
95 | void onFinish(String response);
96 |
97 | /**
98 | * 回调失败。
99 | *
100 | * @param e 异常回调
101 | */
102 | void onError(Exception e);
103 | }
104 | }
105 |
106 |
107 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/http/net/HttpPostUtil.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.http.net;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.io.OutputStream;
8 | import java.io.UnsupportedEncodingException;
9 | import java.net.HttpURLConnection;
10 | import java.net.ProtocolException;
11 | import java.net.URL;
12 | import java.net.URLEncoder;
13 | import java.util.Map;
14 | import java.util.Objects;
15 |
16 | /**
17 | * post 请求工具类封装。使用的是标准的 java 接口:java.net
18 | *
19 | * @author xzy
20 | */
21 | @SuppressWarnings("unused")
22 | class HttpPostUtil {
23 |
24 | static void postRequest(String path, Map params,
25 | HttpCallBackListener httpCallBackListener) {
26 | StringBuilder sb = new StringBuilder();
27 | if (params != null && !params.isEmpty()) {
28 | for (Map.Entry entry : params.entrySet()) {
29 | sb.append(entry.getKey()).append("=");
30 | try {
31 | sb.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
32 | } catch (UnsupportedEncodingException e) {
33 | if (httpCallBackListener != null) {
34 | httpCallBackListener.onError(e);
35 | }
36 | }
37 | sb.append("&");
38 | }
39 | sb.deleteCharAt(sb.length() - 1);
40 | }
41 | byte[] data = sb.toString().getBytes();
42 | HttpURLConnection conn = null;
43 | try {
44 | conn = (HttpURLConnection) new URL(path).openConnection();
45 | } catch (IOException e) {
46 | if (httpCallBackListener != null) {
47 | httpCallBackListener.onError(e);
48 | }
49 | }
50 | Objects.requireNonNull(conn).setConnectTimeout(5000);
51 | try {
52 | conn.setRequestMethod("POST");
53 | } catch (ProtocolException e) {
54 | if (httpCallBackListener != null) {
55 | httpCallBackListener.onError(e);
56 | }
57 | }
58 | conn.setDoOutput(true);
59 | conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
60 | conn.setRequestProperty("Content-Length", data.length + "");
61 | OutputStream outStream = null;
62 | try {
63 | outStream = conn.getOutputStream();
64 | } catch (IOException e) {
65 | if (httpCallBackListener != null) {
66 | httpCallBackListener.onError(e);
67 | }
68 | }
69 | try {
70 | Objects.requireNonNull(outStream).write(data);
71 | outStream.flush();
72 | if (conn.getResponseCode() == 200) {
73 | InputStream in = conn.getInputStream();
74 | BufferedReader bufferReader = new BufferedReader(new InputStreamReader(in));
75 | String line;
76 | StringBuilder response = new StringBuilder();
77 | while ((line = bufferReader.readLine()) != null) {
78 | response.append(line);
79 | }
80 | if (httpCallBackListener != null) {
81 | httpCallBackListener.onFinish(response.toString());
82 | }
83 | }
84 | } catch (IOException e) {
85 | if (httpCallBackListener != null) {
86 | httpCallBackListener.onError(e);
87 | }
88 | } finally {
89 | conn.disconnect();
90 | }
91 | }
92 |
93 | /**
94 | * 回调接口。
95 | */
96 | interface HttpCallBackListener {
97 |
98 | /**
99 | * 回调成功。
100 | *
101 | * @param response 成功响应
102 | */
103 | void onFinish(String response);
104 |
105 | /**
106 | * 回调失败。
107 | *
108 | * @param e 异常回调
109 | */
110 | void onError(Exception e);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/initprovider/InitProvider.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.initprovider
2 |
3 | import android.content.ContentProvider
4 | import android.content.ContentValues
5 | import android.content.Context
6 | import android.content.pm.ProviderInfo
7 | import android.database.Cursor
8 | import android.net.Uri
9 | import com.xzy.utils.exception.unexpectedValue
10 | import com.xzy.utils.exception.unsupported
11 |
12 | /** Base class for [ContentProvider]s used for initialization purposes. */
13 | abstract class InitProvider : ContentProvider() {
14 |
15 | private val emptyApplicationIdProviderAuthority = "com.xzy.util.initprovider"
16 |
17 | override fun attachInfo(context: Context?, info: ProviderInfo?) {
18 | checkContentProviderAuthority(info)
19 | super.attachInfo(context, info)
20 | }
21 |
22 | override fun insert(p0: Uri, p1: ContentValues?): Uri? = unsupported()
23 |
24 | override fun query(
25 | p0: Uri,
26 | p1: Array?,
27 | p2: String?,
28 | p3: Array?,
29 | p4: String?
30 | ): Cursor? = unsupported()
31 |
32 | override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array?): Int =
33 | unsupported()
34 |
35 | override fun delete(p0: Uri, p1: String?, p2: Array?): Int = unsupported()
36 |
37 | override fun getType(p0: Uri): String? = unsupported()
38 |
39 | private fun checkContentProviderAuthority(info: ProviderInfo?) {
40 | checkNotNull(info)
41 | if (emptyApplicationIdProviderAuthority == info.authority) {
42 | unexpectedValue(
43 | "Incorrect provider authority in manifest. Most likely due to a missing " +
44 | "applicationId variable in application's build.gradle."
45 | )
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/io/IO.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.io
4 |
5 | import java.io.Closeable
6 | import java.io.IOException
7 |
8 | /** Do safe action on a [Closeable]. */
9 | fun T.doSafe(action: T.() -> Unit) {
10 | try {
11 | action()
12 | } catch (ignored: IOException) {
13 | } finally {
14 | closeQuietly()
15 | }
16 | }
17 |
18 | /** Close a [Closeable] unconditionally. */
19 | fun T?.closeQuietly() {
20 | try {
21 | this?.close()
22 | } catch (ignored: Exception) {
23 | }
24 | }
25 |
26 | /** Close all [Closeable]s of array after [block] is operated. */
27 | inline fun Array.use(block: () -> Unit) {
28 | var exception: Throwable? = null
29 | try {
30 | return block()
31 | } catch (e: Throwable) {
32 | exception = e
33 | throw e
34 | } finally {
35 | when (exception) {
36 | null -> forEach { it?.close() }
37 | else -> forEach {
38 | try {
39 | it?.close()
40 | } catch (closeException: Throwable) {
41 | exception.addSuppressed(closeException)
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/json/Functions.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.json
2 |
3 | import android.content.Context
4 | import java.io.BufferedReader
5 | import java.io.InputStreamReader
6 |
7 | @Suppress("unused")
8 | object JsonReadFile {
9 | // xxx.json 文件路径为 res/raw/xxx.json
10 | // 调用方式 JsonReadFile.getFromRaw(context,R.raw.xxx)
11 | fun getFromRaw(context: Context, rawID: Int): String? {
12 | try {
13 | val inputReader =
14 | InputStreamReader(context.resources.openRawResource(rawID))
15 | val bufReader = BufferedReader(inputReader)
16 | var line: String?
17 | var result: String? = ""
18 | while (bufReader.readLine().also { line = it } != null) result += line
19 | return result
20 | } catch (e: Exception) {
21 | e.printStackTrace()
22 | }
23 | return ""
24 | }
25 |
26 | // 注意 assets 文件夹和 res 同级,传入上下文和 asset 资源文件名
27 | fun getFromAssets(context: Context, fileName: String): String? {
28 | try {
29 | val inputReader =
30 | InputStreamReader(context.resources.assets.open(fileName))
31 | val bufReader = BufferedReader(inputReader)
32 | var line: String?
33 | var result: String? = ""
34 | while (bufReader.readLine().also { line = it } != null) result += line
35 | return result
36 | } catch (e: Exception) {
37 | e.printStackTrace()
38 | }
39 | return ""
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/keyboard/TouchEmptyCloseKeyBoardUtils.kt:
--------------------------------------------------------------------------------
1 |
2 | package com.xzy.utils.keyboard
3 |
4 | import android.app.Activity
5 | import android.content.Context
6 | import android.graphics.Rect
7 | import android.view.MotionEvent
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import android.view.ViewGroup.FOCUS_BEFORE_DESCENDANTS
11 | import android.view.inputmethod.InputMethodManager
12 | import android.widget.EditText
13 | @Suppress("unused")
14 | class TouchEmptyCloseKeyBoardUtils {
15 | private var outLocation = IntArray(2)
16 | private var rect = Rect()
17 | private val filterViews = mutableListOf()
18 |
19 | fun autoClose(activity: Activity, ev: MotionEvent): Boolean {
20 | // 判断是down事件才处理
21 | if (ev.action != MotionEvent.ACTION_DOWN)
22 | return false
23 | // 获取当前Activity的FocusView和DecorView
24 | val focusView = activity.window.currentFocus ?: return false
25 | val decorView = activity.window.decorView
26 | if (!judgeIsTouchInView(decorView, ev))
27 | return false
28 | if (decorView is ViewGroup) {
29 | val view = getChild(decorView, ev)
30 | // 如果点击的view不是EditText并用不是过滤的view则收起键盘
31 | if (view !is EditText && !filterViews.contains(view)) {
32 | decorView.isFocusable = true
33 | decorView.isFocusableInTouchMode = true
34 | decorView.descendantFocusability = FOCUS_BEFORE_DESCENDANTS
35 | focusView.clearFocus()
36 | closeKeyboard(focusView, focusView.context)
37 | return true
38 | }
39 | }
40 | return false
41 | }
42 |
43 | /**
44 | * 判断点击位置是否落在view上
45 | */
46 | private fun judgeIsTouchInView(view: View, ev: MotionEvent): Boolean {
47 | view.getLocationInWindow(outLocation)
48 | rect.left = outLocation[0]
49 | rect.top = outLocation[1]
50 | rect.right = rect.left + view.width
51 | rect.bottom = rect.top + view.height
52 | return rect.contains(ev.rawX.toInt(), ev.rawY.toInt())
53 | }
54 |
55 | /**
56 | * 递归遍历查找第一个获得该点击事件的View
57 | */
58 | private fun getChild(viewGroup: ViewGroup, ev: MotionEvent): View? {
59 | if (viewGroup.childCount == 0) {
60 | return viewGroup
61 | }
62 | (viewGroup.childCount - 1 downTo 0)
63 | .map { viewGroup.getChildAt(it) }
64 | .filter { judgeIsTouchInView(it, ev) }
65 | .forEach {
66 | if (it is ViewGroup) {
67 | val touchView = getChild(it, ev)
68 | if (touchView != null && touchView.touchables.size > 0) {
69 | return touchView
70 | }
71 | } else {
72 | if (it.touchables.size > 0)
73 | return it
74 | }
75 | }
76 | return null
77 | }
78 |
79 | private fun closeKeyboard(view: View?, mContext: Context) {
80 | val imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
81 | if (view != null) {
82 | imm.hideSoftInputFromWindow(view.windowToken, 0)
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/mask/MaskUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.mask;
2 |
3 | import android.app.Activity;
4 | import android.os.Looper;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.view.Window;
8 |
9 | import androidx.annotation.LayoutRes;
10 |
11 | import java.util.WeakHashMap;
12 |
13 | /**
14 | * 可用于动态为Activity添加遮罩层,悬浮窗等。
15 | * @author xzy
16 | */
17 | @SuppressWarnings("unused")
18 | public class MaskUtils {
19 | private static WeakHashMap viewMap = new WeakHashMap<>();
20 |
21 | public synchronized static void show(final Activity activity, @LayoutRes final int layoutId) {
22 | if (idMainThread()) {
23 | addView(activity, layoutId);
24 | } else {
25 | activity.runOnUiThread(new Runnable() {
26 | @Override
27 | public void run() {
28 | addView(activity, layoutId);
29 | }
30 | });
31 | }
32 | }
33 |
34 |
35 | public synchronized static void show(final Activity activity, final View view) {
36 | if (idMainThread()) {
37 | addView(activity, view);
38 | } else {
39 | activity.runOnUiThread(new Runnable() {
40 | @Override
41 | public void run() {
42 | addView(activity, view);
43 | }
44 | });
45 | }
46 | }
47 |
48 | public synchronized static void show(final Window window, @LayoutRes final int viewId) {
49 | if (idMainThread()) {
50 | addView(window, viewId);
51 | } else {
52 | window.getDecorView().post(new Runnable() {
53 | @Override
54 | public void run() {
55 | addView(window, viewId);
56 | }
57 | });
58 | }
59 | }
60 |
61 | private static void addView(Activity activity, @LayoutRes int viewIdp) {
62 | addView(activity.getWindow(), viewIdp);
63 | }
64 |
65 | private static void addView(Activity activity, View view) {
66 | addView(activity.getWindow(), view);
67 | }
68 |
69 | private static void addView(Window window, @LayoutRes int viewId) {
70 | View mask = window.getLayoutInflater().inflate(viewId, null);
71 | addView(window, mask);
72 | }
73 |
74 | private static void addView(Window window, View mask) {
75 | viewMap.put(window, mask);
76 | ViewGroup decorView = (ViewGroup) window.getDecorView();
77 | decorView.addView(mask);
78 | }
79 |
80 |
81 | public synchronized static void hide(final Activity activity) {
82 | hide(activity.getWindow());
83 | }
84 |
85 | public synchronized static void hide(final Window window) {
86 | if (idMainThread()) {
87 | removeView(window);
88 | } else {
89 | window.getDecorView().post(new Runnable() {
90 | @Override
91 | public void run() {
92 | removeView(window);
93 | }
94 | });
95 | }
96 | }
97 |
98 | private static void removeView(Window window) {
99 | View view = viewMap.get(window);
100 | if (view != null) {
101 | ViewGroup decorView = (ViewGroup) window.getDecorView();
102 | try {
103 | decorView.removeView(view);
104 | } catch (Exception e) {
105 | e.printStackTrace();
106 | }
107 | }
108 | }
109 |
110 | private static boolean idMainThread() {
111 | return Looper.myLooper() == Looper.getMainLooper();
112 | }
113 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/md5/MD5Utils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.md5;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.security.MessageDigest;
5 | import java.security.NoSuchAlgorithmException;
6 |
7 | /**
8 | * md5 工具类。
9 | *
10 | * @author xzy
11 | */
12 | @SuppressWarnings("unused")
13 | public class MD5Utils {
14 |
15 | public static String MD5(String s) {
16 | char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
17 | 'a', 'b', 'c', 'd', 'e', 'f'};
18 | try {
19 | byte[] btInput = s.getBytes();
20 | // 获得 MD5 摘要算法的 MessageDigest 对象
21 | MessageDigest mdInst = MessageDigest.getInstance("MD5");
22 | // 使用指定的字节更新摘要
23 | mdInst.update(btInput);
24 | // 获得密文
25 | byte[] md = mdInst.digest();
26 | // 把密文转换成十六进制的字符串形式
27 | int j = md.length;
28 | char[] str = new char[j * 2];
29 | int k = 0;
30 | for (byte byte0 : md) {
31 | str[k++] = hexDigits[byte0 >>> 4 & 0xf];
32 | str[k++] = hexDigits[byte0 & 0xf];
33 | }
34 | return new String(str);
35 | } catch (Exception e) {
36 | e.printStackTrace();
37 | return null;
38 | }
39 | }
40 |
41 | /**
42 | * MD5加密
43 | *
44 | * @param content 需要加密的字符串
45 | * @return String 加密后的字符串
46 | */
47 | public static String md5(String content) {
48 | byte[] hash;
49 | try {
50 | hash = MessageDigest.getInstance("MD5").digest(content
51 | .getBytes(StandardCharsets.UTF_8));
52 | } catch (NoSuchAlgorithmException e) {
53 | throw new RuntimeException("NoSuchAlgorithmException", e);
54 | }
55 |
56 | StringBuilder hex = new StringBuilder(hash.length * 2);
57 | for (byte b : hash) {
58 | if ((b & 0xFF) < 0x10) {
59 | hex.append("0");
60 | }
61 | hex.append(Integer.toHexString(b & 0xFF));
62 | }
63 | return hex.toString();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/network/Mobile4GUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.network;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.os.Build;
6 | import android.telephony.TelephonyManager;
7 | import android.util.Log;
8 |
9 | import java.lang.reflect.InvocationTargetException;
10 | import java.lang.reflect.Method;
11 |
12 | /**
13 | * Created by xzy.
14 | * 4G模块操作工具类
15 | * 参考 https://blog.csdn.net/qq_24800377/article/details/55657048
16 | */
17 |
18 | public class Mobile4GUtils {
19 |
20 | /**
21 | * 获取数据网络状态
22 | * 4.4 及之前的版本
23 | *
24 | *
25 | *
26 | */
27 | public static boolean getMobileEnabled(Context context) {
28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
29 | TelephonyManager telephonyService = (TelephonyManager) context
30 | .getSystemService(Context.TELEPHONY_SERVICE);
31 | try {
32 | Method getMobileDataEnabledMethod = telephonyService.getClass().getMethod("getDataEnabled");
33 | if (null != getMobileDataEnabledMethod) {
34 | return (boolean) getMobileDataEnabledMethod.invoke(telephonyService);
35 | }
36 | } catch (Exception e) {
37 | Log.e("InstallActivity", "Errot setting"
38 | + ((InvocationTargetException) e).getTargetException()
39 | + telephonyService);
40 | }
41 | return false;
42 | }else {
43 | ConnectivityManager conManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
44 | Class[] getArgArray = null;
45 | Object[] getArgInvoke = null;
46 | try {
47 | Method mGetMethod = conManager.getClass().getMethod("getMobileDataEnabled", getArgArray);
48 | boolean isOpen = (Boolean) mGetMethod.invoke(conManager, getArgInvoke);//获取状态
49 | return isOpen;
50 | } catch (Exception e) {
51 | e.printStackTrace();
52 | }
53 | return false;
54 | }
55 | }
56 |
57 | /**
58 | * 设置移动网络开/关
59 | *
60 | * @param context
61 | * @param isopen
62 | */
63 | public static void setMobileData(Context context, boolean isopen) {
64 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
65 | TelephonyManager telephonyService = (TelephonyManager) context
66 | .getSystemService(Context.TELEPHONY_SERVICE);
67 | try {
68 | Method setMobileDataEnabledMethod = telephonyService.getClass()
69 | .getDeclaredMethod("setDataEnabled", boolean.class);
70 | if (null != setMobileDataEnabledMethod) {
71 | setMobileDataEnabledMethod.invoke(telephonyService,isopen);
72 | }
73 | } catch (Exception e) {
74 | Log.e("InstallActivity", "Errot setting"
75 | + ((InvocationTargetException) e).getTargetException()
76 | + telephonyService);
77 | }
78 | } else {
79 | ConnectivityManager conManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
80 | Class[] setArgArray = new Class[]{boolean.class};
81 | try {
82 | Method mSetMethod = conManager.getClass().getMethod("setMobileDataEnabled", setArgArray);
83 | mSetMethod.invoke(conManager, isopen);
84 | } catch (Exception e) {
85 | e.printStackTrace();
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/network/Net.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.xzy.utils.network
4 |
5 | import android.annotation.SuppressLint
6 | import android.content.Context
7 | import android.content.Context.CONNECTIVITY_SERVICE
8 | import android.net.ConnectivityManager
9 | import android.net.NetworkCapabilities
10 | import android.os.Build
11 | import com.xzy.utils.os.osVerCode
12 |
13 | enum class NetworkType {
14 | NONE,
15 | WIFI,
16 | MOBILE,
17 | ETHERNET,
18 | OTHER
19 | }
20 |
21 | @Suppress("DEPRECATION")
22 | @SuppressLint("NewApi")
23 | private fun Context.networkType(): NetworkType {
24 | val cm = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
25 |
26 | if (osVerCode >= Build.VERSION_CODES.M) {
27 | val capabilities = cm.getNetworkCapabilities(cm.activeNetwork)
28 | if (capabilities == null ||
29 | !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
30 | ) return NetworkType.NONE
31 | return when {
32 | capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkType.WIFI
33 | capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkType.MOBILE
34 | capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ->
35 | NetworkType.ETHERNET
36 | else -> NetworkType.OTHER
37 | }
38 | } else {
39 | val info = cm.activeNetworkInfo
40 | if (info == null || !info.isConnected) {
41 | return NetworkType.NONE
42 | }
43 | @Suppress("DEPRECATION")
44 | return when (info.type) {
45 | ConnectivityManager.TYPE_WIFI -> NetworkType.WIFI
46 | ConnectivityManager.TYPE_MOBILE -> NetworkType.MOBILE
47 | ConnectivityManager.TYPE_ETHERNET -> NetworkType.ETHERNET
48 | else -> NetworkType.OTHER
49 | }
50 | }
51 | }
52 |
53 | /** Check if the network is connected. */
54 | fun Context.isConnected(): Boolean {
55 | return networkType() != NetworkType.NONE
56 | }
57 |
58 | /** Check if it is connected to WiFi. */
59 | fun Context.isWiFi(): Boolean {
60 | return networkType() == NetworkType.WIFI
61 | }
62 |
63 | /** Check if it is connected to mobile. */
64 | fun Context.isMobile(): Boolean {
65 | return networkType() == NetworkType.MOBILE
66 | }
67 |
68 | /** Check if it is connected to ethernet. */
69 | fun Context.isEthernet(): Boolean {
70 | return networkType() == NetworkType.ETHERNET
71 | }
72 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/os/Package.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 | package com.xzy.utils.os
3 |
4 | import android.content.ComponentName
5 | import android.content.pm.PackageManager
6 | import com.xzy.utils.appctx.appCtx
7 |
8 | /** All installed apps. */
9 | val installedApps: List
10 | get() {
11 | val pm = appCtx.packageManager
12 | val packages = pm.getInstalledPackages(0)
13 |
14 | val apps = mutableListOf()
15 | packages.forEach {
16 | @Suppress("UNNECESSARY_SAFE_CALL") val name =
17 | pm.getApplicationLabel(it.applicationInfo)?.toString() ?: ""
18 | apps.add(AppInfo(name, it.packageName, it.versionName ?: ""))
19 | }
20 | return apps
21 | }
22 |
23 | /** Check app of which package named [pkgName] whether been installed. */
24 | fun isInstalled(pkgName: String): Boolean {
25 | return try {
26 | appCtx.packageManager.getPackageInfo(pkgName, 0) != null
27 | } catch (e: Exception) {
28 | false
29 | }
30 | }
31 |
32 | /** Check app whether be in main process. */
33 | fun isMainProcess() = appCtx.packageName == processName
34 |
35 | /** Check if component [clazz] is enabled. */
36 | fun isComponentEnabled(clazz: Class<*>): Boolean {
37 | val componentName = ComponentName(appCtx, clazz)
38 | return appCtx.packageManager.getComponentEnabledSetting(componentName) !=
39 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED
40 | }
41 |
42 | /** Check if component [clazz] is disabled. */
43 | fun isComponentDisabled(clazz: Class<*>): Boolean {
44 | val componentName = ComponentName(appCtx, clazz)
45 | return appCtx.packageManager.getComponentEnabledSetting(componentName) ==
46 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED
47 | }
48 |
49 | /** Enable component. */
50 | fun enableComponent(clazz: Class<*>) {
51 | setComponentState(clazz, true)
52 | }
53 |
54 | /** Disable component. */
55 | fun disableComponent(clazz: Class<*>) {
56 | setComponentState(clazz, false)
57 | }
58 |
59 | private fun setComponentState(clazz: Class<*>, enable: Boolean) {
60 | val componentName = ComponentName(appCtx, clazz)
61 | val pm = appCtx.packageManager
62 | val oldState = pm.getComponentEnabledSetting(componentName)
63 | val newState = if (enable)
64 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED
65 | else
66 | PackageManager.COMPONENT_ENABLED_STATE_DISABLED
67 | if (newState != oldState) {
68 | val flags = PackageManager.DONT_KILL_APP
69 | pm.setComponentEnabledSetting(componentName, newState, flags)
70 | }
71 | }
72 |
73 | @Suppress("unused")
74 | class AppInfo(val name: String, val pkgName: String, val versionName: String)
75 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/permission/FloatTool.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.permission;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.provider.Settings;
10 |
11 | import androidx.annotation.RequiresApi;
12 |
13 | /**
14 | * 参考 https://blog.csdn.net/scimence/article/details/101050780https://blog.csdn.net/scimence/article/details/101050780
15 | * FloatTool.java:Android 6.0 之后应用悬浮窗权限请求
16 | *
17 | * AndroidMainifest.xml中添加:
18 | *
19 | * 用法:
20 | * 1、请求悬浮窗权限:FloatTool.RequestOverlayPermission(this);
21 | * 2、处理悬浮窗权限请求结果:FloatTool.onActivityResult(requestCode, resultCode, data, this);
22 | *
23 | * @author xzy
24 | */
25 | public class FloatTool {
26 | public static boolean CanShowFloat = false;
27 |
28 | private static final int REQUEST_OVERLAY = 5004;
29 |
30 | /**
31 | * 动态请求悬浮窗权限
32 | */
33 | public static void requestOverlayPermission(Activity instance) {
34 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
35 | if (!Settings.canDrawOverlays(instance)) {
36 | Intent intent = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION"
37 | , Uri.parse("package:" + instance.getPackageName()));
38 | instance.startActivityForResult(intent, REQUEST_OVERLAY);
39 | } else {
40 | CanShowFloat = true;
41 | }
42 | }
43 | }
44 |
45 | /**
46 | * 浮窗权限请求,Activity执行结果,回调函数
47 | */
48 | @RequiresApi(api = Build.VERSION_CODES.M)
49 | public static void onActivityResult(int requestCode, int resultCode, Intent data, final Activity instance) {
50 | if (requestCode == REQUEST_OVERLAY) {
51 | if (resultCode == Activity.RESULT_OK) {
52 | // 设置标识为可显示悬浮窗
53 | CanShowFloat = true;
54 | } else {
55 | CanShowFloat = false;
56 | // 若当前未允许显示悬浮窗,则提示授权
57 | if (!Settings.canDrawOverlays(instance)) {
58 | AlertDialog.Builder builder = new AlertDialog.Builder(instance);
59 | builder.setCancelable(false);
60 | builder.setTitle("悬浮窗权限未授权");
61 | builder.setMessage("应用需要悬浮窗权限,以展示浮标");
62 | builder.setPositiveButton("去添加 权限", new DialogInterface.OnClickListener() {
63 | @Override
64 | public void onClick(DialogInterface dialog, int which) {
65 | dialog.dismiss();
66 | requestOverlayPermission(instance);
67 | }
68 | });
69 |
70 | builder.setNegativeButton("拒绝则 退出", new DialogInterface.OnClickListener() {
71 | @Override
72 | public void onClick(DialogInterface dialog, int which) {
73 | dialog.dismiss();
74 | // 若拒绝了所需的权限请求,则退出应用
75 | instance.finish();
76 | System.exit(0);
77 | }
78 | });
79 | builder.show();
80 | }
81 | }
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/permission/realize_1/OnPermissionListener.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.permission.realize_1;
2 |
3 | /**
4 | * 权限请求监听
5 | */
6 | public interface OnPermissionListener {
7 | /**
8 | * 用户同意授权
9 | *
10 | * @param requestCode 请求码
11 | * @param grantPermissions 已被授权的权限
12 | */
13 | void onPermissionGranted(int requestCode, String[] grantPermissions);
14 |
15 | /**
16 | * 用户拒绝授权
17 | *
18 | * @param requestCode 请求码
19 | * @param deniedPermissions 被拒绝授权的权限
20 | * @param hasAlwaysDenied 用户是否在权限弹出框中勾选了“总是拒绝授权”
21 | */
22 | void onPermissionDenied(int requestCode, String[] deniedPermissions, boolean hasAlwaysDenied);
23 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/permission/realize_1/PermissionRequest.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.permission.realize_1;
2 |
3 | import android.app.Activity;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.core.app.ActivityCompat;
7 |
8 | /**
9 | * 权限请求类
10 | */
11 | @SuppressWarnings("all")
12 | public class PermissionRequest {
13 | private Activity mActivity;
14 | private int mRequestCode;
15 | private String[] mPermissions;
16 | private boolean isAutoShowTip;
17 | private OnPermissionListener mListener;
18 |
19 | public PermissionRequest(@NonNull Activity activity) {
20 | this.mActivity = activity;
21 | }
22 |
23 | /**
24 | * 设置权限请求的请求码
25 | *
26 | * @param requestCode 权限请求码
27 | * @return {@link PermissionRequest}
28 | */
29 | public PermissionRequest requestCode(int requestCode) {
30 | this.mRequestCode = requestCode;
31 | return this;
32 | }
33 |
34 | /**
35 | * 设置需要申请的权限
36 | *
37 | * @param permissions 需要申请的权限
38 | * @return {@link PermissionRequest}
39 | */
40 | public PermissionRequest permissions(@NonNull String... permissions) {
41 | this.mPermissions = permissions;
42 | return this;
43 | }
44 |
45 | /**
46 | * 设置权限请求的回调接口
47 | *
48 | * @param listener 权限请求的回调接口
49 | * @return {@link PermissionRequest}
50 | */
51 | public PermissionRequest callback(OnPermissionListener listener) {
52 | this.mListener = listener;
53 | return this;
54 | }
55 |
56 | /**
57 | * 是否自动显示拒绝授权时的提示
58 | *
59 | * @param isAutoShowTip {@code true}: 显示
{@code false}: 不显示
60 | * @return {@link PermissionRequest}
61 | */
62 | public PermissionRequest autoShowTip(boolean isAutoShowTip) {
63 | this.isAutoShowTip = isAutoShowTip;
64 | return this;
65 | }
66 |
67 | /**
68 | * 执行权限请求
69 | */
70 | public void execute() {
71 | if (PermissionUtils.isNeedRequest()) {
72 | String[] deniedPermissions = PermissionUtils
73 | .getDeniedPermissions(mActivity, mPermissions);
74 | if (deniedPermissions.length > 0) {
75 | ActivityCompat.requestPermissions(mActivity, deniedPermissions, mRequestCode);
76 | } else {
77 | if (mListener != null) {
78 | mListener.onPermissionGranted(mRequestCode, mPermissions);
79 | }
80 | }
81 | } else {
82 | if (mListener != null) {
83 | mListener.onPermissionGranted(mRequestCode, mPermissions);
84 | }
85 | }
86 | }
87 |
88 | /**
89 | * 获取权限请求码
90 | *
91 | * @return 权限请求码
92 | */
93 | public int getRequestCode() {
94 | return mRequestCode;
95 | }
96 |
97 | /**
98 | * 获取申请的权限
99 | *
100 | * @return 申请的权限
101 | */
102 | public String[] getPermissions() {
103 | return mPermissions;
104 | }
105 |
106 | /**
107 | * 获取是否自动显示拒绝授权时的提示
108 | *
109 | * @return {@code true}: 显示
{@code false}: 不显示
110 | */
111 | public boolean isAutoShowTip() {
112 | return isAutoShowTip;
113 | }
114 |
115 | /**
116 | * 获取权限请求的回调方法
117 | *
118 | * @return 权限请求的回调
119 | */
120 | public OnPermissionListener getPermissionListener() {
121 | return mListener;
122 | }
123 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/resource/ColorResources.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE", "unused")
2 |
3 | package com.xzy.utils.resource
4 |
5 | import android.annotation.SuppressLint
6 | import android.content.Context
7 | import android.content.res.ColorStateList
8 | import android.graphics.Color
9 | import android.view.View
10 | import androidx.annotation.AttrRes
11 | import androidx.annotation.ColorInt
12 | import androidx.annotation.ColorRes
13 | import androidx.core.content.ContextCompat
14 | import androidx.core.content.res.ResourcesCompat
15 | import androidx.fragment.app.Fragment
16 | import com.xzy.utils.appctx.appCtx
17 |
18 | @SuppressLint("NewApi")
19 | @ColorInt
20 | fun Context.color(@ColorRes colorRes: Int): Int = ContextCompat.getColor(this, colorRes)
21 |
22 | inline fun Fragment.color(@ColorRes colorRes: Int) = requireContext().color(colorRes)
23 |
24 | inline fun View.color(@ColorRes colorRes: Int) = context.color(colorRes)
25 |
26 | /**
27 | * Use this method for non configuration dependent resources when you don't have a [Context]
28 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
29 | * initialized yet).
30 | *
31 | * For theme dependent resources, the application theme will be implicitly used.
32 | */
33 | inline fun appColor(@ColorRes colorRes: Int) = appCtx.color(colorRes)
34 |
35 | fun Context.colorSL(@ColorRes colorRes: Int): ColorStateList? =
36 | ResourcesCompat.getColorStateList(resources, colorRes, theme)
37 |
38 | inline fun Fragment.colorSL(@ColorRes colorRes: Int) = requireContext().colorSL(colorRes)
39 |
40 | inline fun View.colorSL(@ColorRes colorRes: Int) = context.colorSL(colorRes)
41 |
42 | /**
43 | * Use this method for non configuration dependent resources when you don't have a [Context]
44 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
45 | * initialized yet).
46 | *
47 | * For theme dependent resources, the application theme will be implicitly used.
48 | */
49 | inline fun appColorSL(@ColorRes colorRes: Int) = appCtx.colorSL(colorRes)
50 |
51 | // Styled resources below
52 |
53 | private inline val defaultColor get() = Color.RED
54 |
55 | @ColorInt
56 | fun Context.styledColor(@AttrRes attr: Int): Int =
57 | withStyledAttributes(attr) { getColor(it, defaultColor) }
58 |
59 | inline fun Fragment.styledColor(@AttrRes attr: Int) = requireContext().styledColor(attr)
60 |
61 | inline fun View.styledColor(@AttrRes attr: Int) = context.styledColor(attr)
62 |
63 | /**
64 | * Use this method for non configuration dependent resources when you don't have a [Context]
65 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
66 | * initialized yet).
67 | *
68 | * For theme dependent resources, the application theme will be implicitly used.
69 | */
70 | inline fun appStyledColor(@AttrRes attr: Int) = appCtx.styledColor(attr)
71 |
72 | fun Context.styledColorSL(@AttrRes attr: Int): ColorStateList? =
73 | withStyledAttributes(attr) { getColorStateList(it) }
74 |
75 | inline fun Fragment.styledColorSL(@AttrRes attr: Int) = context!!.styledColorSL(attr)
76 |
77 | inline fun View.styledColorSL(@AttrRes attr: Int) = context.styledColorSL(attr)
78 |
79 | /**
80 | * Use this method for non configuration dependent resources when you don't have a [Context]
81 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
82 | * initialized yet).
83 | *
84 | * For theme dependent resources, the application theme will be implicitly used.
85 | */
86 | inline fun appStyledColorSL(@AttrRes attr: Int) = appCtx.styledColorSL(attr)
87 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/resource/DrawableResources.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE", "unused")
2 |
3 | package com.xzy.utils.resource
4 |
5 | import android.content.Context
6 | import android.graphics.drawable.Drawable
7 | import android.view.View
8 | import androidx.annotation.AttrRes
9 | import androidx.annotation.DrawableRes
10 | import androidx.core.content.res.ResourcesCompat
11 | import androidx.fragment.app.Fragment
12 | import com.xzy.utils.appctx.appCtx
13 |
14 | fun Context.drawable(@DrawableRes drawableResId: Int): Drawable? =
15 | ResourcesCompat.getDrawable(resources, drawableResId, theme)
16 |
17 | inline fun Fragment.drawable(@DrawableRes drawableResId: Int) =
18 | requireContext().drawable(drawableResId)
19 |
20 | inline fun View.drawable(@DrawableRes drawableResId: Int) = context.drawable(drawableResId)
21 |
22 | /**
23 | * Use this method for non configuration dependent resources when you don't have a [Context]
24 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
25 | * initialized yet).
26 | *
27 | * For theme dependent resources, the application theme will be implicitly used.
28 | */
29 | inline fun appDrawable(@DrawableRes drawableResId: Int) = appCtx.drawable(drawableResId)
30 |
31 | // Styled resources below
32 |
33 | fun Context.styledDrawable(@AttrRes attr: Int): Drawable? =
34 | withStyledAttributes(attr) { getDrawable(it) }
35 |
36 | inline fun Fragment.styledDrawable(@AttrRes attr: Int) = context!!.styledDrawable(attr)
37 |
38 | inline fun View.styledDrawable(@AttrRes attr: Int) = context.styledDrawable(attr)
39 |
40 | /**
41 | * Use this method for non configuration dependent resources when you don't have a [Context]
42 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
43 | * initialized yet).
44 | *
45 | * For theme dependent resources, the application theme will be implicitly used.
46 | */
47 | inline fun appStyledDrawable(@AttrRes attr: Int) = appCtx.styledDrawable(attr)
48 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/resource/PrimitiveResources.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE", "unused")
2 |
3 | package com.xzy.utils.resource
4 |
5 | import android.content.Context
6 | import android.view.View
7 | import androidx.annotation.ArrayRes
8 | import androidx.annotation.AttrRes
9 | import androidx.annotation.BoolRes
10 | import androidx.annotation.IntegerRes
11 | import androidx.fragment.app.Fragment
12 | import com.xzy.utils.appctx.appCtx
13 |
14 | inline fun Context.bool(@BoolRes boolResId: Int): Boolean = resources.getBoolean(boolResId)
15 |
16 | inline fun Fragment.bool(@BoolRes boolResId: Int) = requireContext().bool(boolResId)
17 |
18 | inline fun View.bool(@BoolRes boolResId: Int) = context.bool(boolResId)
19 |
20 | /**
21 | * Use this method for non configuration dependent resources when you don't have a [Context]
22 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
23 | * initialized yet).
24 | *
25 | * For theme dependent resources, the application theme will be implicitly used.
26 | */
27 | inline fun appBool(@BoolRes boolResId: Int) = appCtx.bool(boolResId)
28 |
29 | inline fun Context.int(@IntegerRes intResId: Int): Int = resources.getInteger(intResId)
30 |
31 | inline fun Fragment.int(@IntegerRes intResId: Int) = requireContext().int(intResId)
32 |
33 | inline fun View.int(@IntegerRes intResId: Int) = context.int(intResId)
34 |
35 | /**
36 | * Use this method for non configuration dependent resources when you don't have a [Context]
37 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
38 | * initialized yet).
39 | *
40 | * For theme dependent resources, the application theme will be implicitly used.
41 | */
42 | inline fun appInt(@IntegerRes intResId: Int) = appCtx.int(intResId)
43 |
44 | inline fun Context.intArray(@ArrayRes intArrayResId: Int): IntArray =
45 | resources.getIntArray(intArrayResId)
46 |
47 | inline fun Fragment.intArray(@ArrayRes intArrayResId: Int) =
48 | requireContext().intArray(intArrayResId)
49 |
50 | inline fun View.intArray(@ArrayRes intArrayResId: Int) = context.intArray(intArrayResId)
51 |
52 | /**
53 | * Use this method for non configuration dependent resources when you don't have a [Context]
54 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
55 | * initialized yet).
56 | *
57 | * For theme dependent resources, the application theme will be implicitly used.
58 | */
59 | inline fun appIntArray(@ArrayRes intArrayResId: Int) = appCtx.intArray(intArrayResId)
60 |
61 | // Styled resources below
62 |
63 | fun Context.styledBool(@AttrRes attr: Int): Boolean =
64 | withStyledAttributes(attr) { getBoolean(it, false) }
65 |
66 | inline fun Fragment.styledBool(@AttrRes attr: Int) = requireContext().styledBool(attr)
67 |
68 | inline fun View.styledBool(@AttrRes attr: Int) = context.styledBool(attr)
69 |
70 | /**
71 | * Use this method for non configuration dependent resources when you don't have a [Context]
72 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
73 | * initialized yet).
74 | *
75 | * For theme dependent resources, the application theme will be implicitly used.
76 | */
77 | inline fun appStyledBool(@AttrRes attr: Int) = appCtx.styledBool(attr)
78 |
79 | fun Context.styledInt(@AttrRes attr: Int): Int = withStyledAttributes(attr) { getInteger(it, -1) }
80 |
81 | inline fun Fragment.styledInt(@AttrRes attr: Int) = requireContext().styledInt(attr)
82 |
83 | inline fun View.styledInt(@AttrRes attr: Int) = context.styledInt(attr)
84 |
85 | /**
86 | * Use this method for non configuration dependent resources when you don't have a [Context]
87 | * or when you're calling it from an Activity or a Fragment member (as the Context is not
88 | * initialized yet).
89 | *
90 | * For theme dependent resources, the application theme will be implicitly used.
91 | */
92 | inline fun appStyledInt(@AttrRes attr: Int) = appCtx.styledInt(attr)
93 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/resource/StyledAttributes.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.resource
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.content.res.TypedArray
6 | import androidx.annotation.AttrRes
7 | import com.xzy.utils.thread.isMainThread
8 |
9 | private val uiThreadConfinedCachedAttrArray = IntArray(1)
10 | private val cachedAttrArray = IntArray(1)
11 |
12 | inline fun Context.withStyledAttributes(
13 | @AttrRes attrRes: Int,
14 | func: TypedArray.(firstIndex: Int) -> T
15 | ): T = obtainStyledAttr(attrRes).let { styledAttrs ->
16 | styledAttrs.func(styledAttrs.getIndex(0)).also { styledAttrs.recycle() }
17 | }
18 |
19 | @PublishedApi
20 | @SuppressLint("Recycle")
21 | internal fun Context.obtainStyledAttr(@AttrRes attrRes: Int): TypedArray = if (isMainThread) {
22 | uiThreadConfinedCachedAttrArray[0] = attrRes
23 | obtainStyledAttributes(uiThreadConfinedCachedAttrArray)
24 | } else synchronized(cachedAttrArray) {
25 | cachedAttrArray[0] = attrRes
26 | obtainStyledAttributes(cachedAttrArray)
27 | }
28 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/shake/ShakeUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.shake;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorEvent;
6 | import android.hardware.SensorEventListener;
7 | import android.hardware.SensorManager;
8 | import android.util.Log;
9 |
10 | /**
11 | * 摇一摇工具类。
12 | * 分别在Activity的onResume和onPause方法中调用该工具类的onResume和onPause方法
13 | *
14 | * @author xzy
15 | */
16 | @SuppressWarnings("unused")
17 | public class ShakeUtils implements SensorEventListener {
18 |
19 | private static final String TAG = "ShakeUtils";
20 |
21 | public ShakeUtils(Context context) {
22 | mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
23 | }
24 |
25 | public void setOnShakeListener(OnShakeListener onShakeListener) {
26 | mOnShakeListener = onShakeListener;
27 | }
28 |
29 |
30 | public void onResume() {
31 | isShake = false;
32 | mSensorManager.registerListener(this,
33 | mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
34 | SensorManager.SENSOR_DELAY_NORMAL);
35 | }
36 |
37 | public void onPause() {
38 | mSensorManager.unregisterListener(this);
39 | }
40 |
41 | //防止多次打开
42 | private boolean isShake = false;
43 |
44 | @Override
45 | public void onSensorChanged(SensorEvent sensorEvent) {
46 | int sensorType = sensorEvent.sensor.getType();
47 | //values[0]:X轴,values[1]:Y轴,values[2]:Z轴
48 | float[] values = sensorEvent.values;
49 | if (sensorType == Sensor.TYPE_ACCELEROMETER) {
50 | //这里可以调节摇一摇的灵敏度
51 | if ((Math.abs(values[0]) > SENSOR_VALUE
52 | || Math.abs(values[1]) > SENSOR_VALUE
53 | || Math.abs(values[2]) > SENSOR_VALUE)) {
54 | Log.e(TAG, "onSensorChanged: " + values[0]
55 | + " " + values[1] + " " + values[2]);
56 | if (null != mOnShakeListener && !isShake) {
57 | mOnShakeListener.onShake();
58 | isShake = true;
59 | }
60 | }
61 | }
62 | }
63 |
64 | @Override
65 | public void onAccuracyChanged(Sensor sensor, int i) {
66 |
67 | }
68 |
69 | public interface OnShakeListener {
70 | void onShake();
71 | }
72 |
73 | private SensorManager mSensorManager;
74 | private OnShakeListener mOnShakeListener = null;
75 | private static final int SENSOR_VALUE = 15;
76 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/shape/ShapeUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.shape;
2 |
3 | import android.graphics.drawable.GradientDrawable;
4 |
5 | /**
6 | * Shape 工具类
7 | * 参考 https://github.com/albert-lii/SUtils/blob/master/sutils/
8 | * src/main/java/com/liyi/sutils/utils/graphic/ShapeUtil.java
9 | *
10 | * @author xzy
11 | */
12 | @SuppressWarnings("unused")
13 | public final class ShapeUtils {
14 |
15 | /**
16 | * 绘制圆角矩形 drawable
17 | *
18 | * @param fillColor 图形填充色
19 | * @param radius 图形圆角半径
20 | * @return 圆角矩形
21 | */
22 | public static GradientDrawable drawRoundRect(int fillColor, int radius) {
23 | GradientDrawable shape = new GradientDrawable();
24 | shape.setShape(GradientDrawable.RECTANGLE);
25 | shape.setColor(fillColor);
26 | shape.setCornerRadius(radius);
27 | return shape;
28 | }
29 |
30 | /**
31 | * 绘制圆角矩形 drawable
32 | *
33 | * @param fillColor 图形填充色
34 | * @param radius 图形圆角半径
35 | * @param strokeWidth 边框的大小
36 | * @param strokeColor 边框的颜色
37 | * @return 圆角矩形
38 | */
39 | public static GradientDrawable drawRoundRect(int fillColor, int radius, int strokeWidth,
40 | int strokeColor) {
41 | GradientDrawable shape = new GradientDrawable();
42 | shape.setShape(GradientDrawable.RECTANGLE);
43 | shape.setColor(fillColor);
44 | shape.setCornerRadius(radius);
45 | shape.setStroke(strokeWidth, strokeColor);
46 | return shape;
47 | }
48 |
49 | /**
50 | * 绘制圆角矩形 drawable
51 | *
52 | * @param fillColor 图形填充色
53 | * @param radii 图形圆角半径
54 | * @param strokeWidth 边框的大小
55 | * @param strokeColor 边框的颜色
56 | * @return 圆角矩形
57 | */
58 | public static GradientDrawable drawRoundRect(int fillColor, float[] radii, int strokeWidth,
59 | int strokeColor) {
60 | GradientDrawable shape = new GradientDrawable();
61 | shape.setShape(GradientDrawable.RECTANGLE);
62 | shape.setColor(fillColor);
63 | shape.setCornerRadii(radii);
64 | shape.setStroke(strokeWidth, strokeColor);
65 | return shape;
66 | }
67 |
68 | /**
69 | * 绘制圆形
70 | *
71 | * @param fillColor 图形填充色
72 | * @param size 图形的大小
73 | * @param strokeWidth 边框的大小
74 | * @param strokeColor 边框的颜色
75 | * @return 圆形
76 | */
77 | public static GradientDrawable drawCircle(int fillColor, int size, int strokeWidth,
78 | int strokeColor) {
79 | GradientDrawable shape = new GradientDrawable();
80 | shape.setShape(GradientDrawable.OVAL);
81 | shape.setSize(size, size);
82 | shape.setColor(fillColor);
83 | shape.setStroke(strokeWidth, strokeColor);
84 | return shape;
85 | }
86 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/sp/Sp.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.sp
2 |
3 | import android.content.SharedPreferences
4 | import android.content.Context.MODE_PRIVATE
5 | import com.google.gson.Gson
6 | import com.google.gson.reflect.TypeToken
7 | import com.xzy.utils.UtilsApp.INSTANCE
8 |
9 |
10 | object Sp {
11 | private var sp: SharedPreferences? = null
12 | /**
13 | * 存入
14 | */
15 | fun put(key: String, userList: List) {
16 | if (sp == null) {
17 | sp = INSTANCE.applicationContext.getSharedPreferences("config", MODE_PRIVATE)
18 | }
19 | val editor = sp?.edit()
20 | val gson = Gson()
21 | val json = gson.toJson(userList)
22 | editor?.putString(key, json)
23 | editor?.apply()
24 | }
25 |
26 | /**
27 | * 读取
28 | */
29 | fun get(key: String): List {
30 | if (sp == null) {
31 | sp = INSTANCE.applicationContext.getSharedPreferences("config", MODE_PRIVATE)
32 | }
33 | val gson = Gson()
34 | val json = sp?.getString(key, null)
35 | val type = object : TypeToken>() {
36 |
37 | }.type
38 | return gson.fromJson(json, type)
39 | }
40 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/sp/User.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.sp;
2 |
3 | public class User {
4 | private int age;
5 | private String name;
6 |
7 | public int getAge() {
8 | return age;
9 | }
10 |
11 | public void setAge(int age) {
12 | this.age = age;
13 | }
14 |
15 | public String getName() {
16 | return name;
17 | }
18 |
19 | public void setName(String name) {
20 | this.name = name;
21 | }
22 |
23 | @Override
24 | public String toString() {
25 | return "User{" +
26 | "age=" + age +
27 | ", name='" + name + '\'' +
28 | '}';
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/sp/kotlin/Edit.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.sp.kotlin
2 |
3 | inline fun P.edit(editions: P.() -> Unit) {
4 | beginEdit()
5 | try {
6 | editions()
7 | endEdit()
8 | } catch (t: Throwable) {
9 | abortEdit()
10 | throw t
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/sp/kotlin/ResettableLazy.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.sp.kotlin
2 |
3 | import kotlin.reflect.KProperty
4 |
5 | /**
6 | * A Lazy delegate that keeps a reference to it's initializer and resets it's value if you set it's
7 | * value with it's current value.
8 | *
9 | * Note that this doesn't support nullable values.
10 | */
11 | class ResettableLazy(private val initializer: () -> T) {
12 |
13 | private var value: T? = null
14 |
15 | operator fun getValue(thisRef: Any?, prop: KProperty<*>): T {
16 | return value ?: initializer().apply { value = this }
17 | }
18 |
19 | operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: T) {
20 | this.value = value
21 | }
22 |
23 | fun reset() {
24 | value = null
25 | }
26 | }
27 |
28 | fun resettableLazy(initializer: () -> T) = ResettableLazy(initializer)
29 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/sp/kotlin/Sp.kt:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.sp.kotlin
2 |
3 | // 实际开发中直接使用 Sp 存储各种类型的值
4 | object Sp : Preferences("projectName") {
5 | var magicNumber by intPref(0) // The property name is used as the key.
6 | var currentLevel by IntPref("currentLevel", 1)
7 | var bossesFought by IntPref("bossBattleVictories", 0)
8 | var lastTimePlayed by LongPref("lastSessionTime", 0L)
9 | var pseudo by StringPref("playerPseudo", "Player 1")
10 | var favoriteCharacter by stringOrNullPref()
11 | }
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/thread/MainThread.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE", "unused")
2 |
3 | package com.xzy.utils.thread
4 |
5 | import android.os.Looper
6 |
7 | /** This main looper cache avoids synchronization overhead when accessed repeatedly. */
8 | @JvmField
9 | val mainLooper: Looper = Looper.getMainLooper()
10 |
11 | @JvmField
12 | val mainThread: Thread = mainLooper.thread
13 |
14 | val isMainThread inline get() = mainThread === Thread.currentThread()
15 |
16 | val currentThreadName: String get() = Thread.currentThread().name
17 |
18 | /**
19 | * Passes if run on the [mainThread] (aka. UI thread), throws an [IllegalStateException] otherwise.
20 | */
21 | inline fun checkMainThread() = check(isMainThread) {
22 | "This should ONLY be called on the main thread! Current: ${Thread.currentThread()}"
23 | }
24 |
25 | /**
26 | * Passes if not run on the [mainThread] (aka. UI thread), throws an [IllegalStateException]
27 | * otherwise.
28 | */
29 | inline fun checkNotMainThread() = check(!isMainThread) {
30 | "This should NEVER be called on the main thread! Current: ${Thread.currentThread()}"
31 | }
32 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/threadpool/ThreadPoolExecutors.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xzy.utils.threadpool;
18 |
19 | import android.os.Handler;
20 | import android.os.Looper;
21 |
22 | import androidx.annotation.NonNull;
23 |
24 | import java.util.concurrent.Executor;
25 | import java.util.concurrent.ExecutorService;
26 | import java.util.concurrent.Executors;
27 |
28 |
29 | /**
30 | * 应用的全局线程池 (包括单线程池的磁盘io,多线程池的网络io和主线程)
31 | *
32 | * @author xzy
33 | * 部分参考 https://github.com/xuexiangjys/XUtil
34 | */
35 | @SuppressWarnings("all")
36 | public class ThreadPoolExecutors {
37 |
38 | private static ThreadPoolExecutors sInstance;
39 |
40 | /**
41 | * 单线程池
42 | */
43 | private final ExecutorService mSingleIO;
44 |
45 | /**
46 | * 多线程池
47 | */
48 | private final ExecutorService mPoolIO;
49 |
50 | /**
51 | * 主线程
52 | */
53 | private final Executor mMainThread;
54 |
55 | private ThreadPoolExecutors(ExecutorService singleIO, ExecutorService poolIO,
56 | Executor mainThread) {
57 | mSingleIO = singleIO;
58 | mPoolIO = poolIO;
59 | mMainThread = mainThread;
60 | }
61 |
62 | private ThreadPoolExecutors() {
63 | this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(Runtime.getRuntime()
64 | .availableProcessors()),
65 | new MainThreadExecutor());
66 | }
67 |
68 | /**
69 | * 获取线程管理实例
70 | *
71 | * @return
72 | */
73 | public static ThreadPoolExecutors get() {
74 | if (sInstance == null) {
75 | synchronized (ThreadPoolExecutors.class) {
76 | if (sInstance == null) {
77 | sInstance = new ThreadPoolExecutors();
78 | }
79 | }
80 | }
81 | return sInstance;
82 | }
83 |
84 | /**
85 | * 获取单线程池
86 | *
87 | * @return
88 | */
89 | public ExecutorService singleIO() {
90 | return mSingleIO;
91 | }
92 |
93 | /**
94 | * 获取磁盘单线程池
95 | *
96 | * @return
97 | */
98 | public ExecutorService diskIO() {
99 | return mSingleIO;
100 | }
101 |
102 | /**
103 | * 获取多线程池
104 | *
105 | * @return
106 | */
107 | public ExecutorService poolIO() {
108 | return mPoolIO;
109 | }
110 |
111 | /**
112 | * 获取网络请求多线程池
113 | *
114 | * @return
115 | */
116 | public ExecutorService networkIO() {
117 | return mPoolIO;
118 | }
119 |
120 | /**
121 | * 获取主线程
122 | *
123 | * @return
124 | */
125 | public Executor mainThread() {
126 | return mMainThread;
127 | }
128 |
129 | private static class MainThreadExecutor implements Executor {
130 | private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
131 |
132 | @Override
133 | public void execute(@NonNull Runnable command) {
134 | mainThreadHandler.post(command);
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/throwable/ThrowableUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.throwable;
2 |
3 | import java.io.PrintWriter;
4 | import java.io.StringWriter;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.StringTokenizer;
8 |
9 | /**
10 | * Throwable 相关的工具类。
11 | * 参考 https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/
12 | * java/com/blankj/utilcode/util/ThrowableUtils.java
13 | *
14 | * @author xzy
15 | */
16 | @SuppressWarnings("unused")
17 | public class ThrowableUtils {
18 |
19 | private static final String LINE_SEP = System.getProperty("line.separator");
20 |
21 | private ThrowableUtils() {
22 | throw new UnsupportedOperationException("u can't instantiate me...");
23 | }
24 |
25 | public static String getFullStackTrace(Throwable throwable) {
26 | final List throwableList = new ArrayList<>();
27 | while (throwable != null && !throwableList.contains(throwable)) {
28 | throwableList.add(throwable);
29 | throwable = throwable.getCause();
30 | }
31 | final int size = throwableList.size();
32 | final List frames = new ArrayList<>();
33 | List nextTrace = getStackFrameList(throwableList.get(size - 1));
34 | for (int i = size; --i >= 0; ) {
35 | final List trace = nextTrace;
36 | if (i != 0) {
37 | nextTrace = getStackFrameList(throwableList.get(i - 1));
38 | removeCommonFrames(trace, nextTrace);
39 | }
40 | if (i == size - 1) {
41 | frames.add(throwableList.get(i).toString());
42 | } else {
43 | frames.add(" Caused by: " + throwableList.get(i).toString());
44 | }
45 | frames.addAll(trace);
46 | }
47 | StringBuilder sb = new StringBuilder();
48 | for (final String element : frames) {
49 | sb.append(element).append(LINE_SEP);
50 | }
51 | return sb.toString();
52 | }
53 |
54 | private static List getStackFrameList(final Throwable throwable) {
55 | final StringWriter sw = new StringWriter();
56 | final PrintWriter pw = new PrintWriter(sw, true);
57 | throwable.printStackTrace(pw);
58 | final String stackTrace = sw.toString();
59 | final StringTokenizer frames = new StringTokenizer(stackTrace, LINE_SEP);
60 | final List list = new ArrayList<>();
61 | boolean traceStarted = false;
62 | while (frames.hasMoreTokens()) {
63 | final String token = frames.nextToken();
64 | // Determine if the line starts with at
65 | final int at = token.indexOf("at");
66 | if (at != -1 && token.substring(0, at).trim().isEmpty()) {
67 | traceStarted = true;
68 | list.add(token);
69 | } else if (traceStarted) {
70 | break;
71 | }
72 | }
73 | return list;
74 | }
75 |
76 | private static void removeCommonFrames(final List causeFrames, final List wrapperFrames) {
77 | int causeFrameIndex = causeFrames.size() - 1;
78 | int wrapperFrameIndex = wrapperFrames.size() - 1;
79 | while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
80 | // Remove the frame from the cause trace if it is the same
81 | // as in the wrapper trace
82 | final String causeFrame = causeFrames.get(causeFrameIndex);
83 | final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
84 | if (causeFrame.equals(wrapperFrame)) {
85 | causeFrames.remove(causeFrameIndex);
86 | }
87 | causeFrameIndex--;
88 | wrapperFrameIndex--;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/toast/Toast.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("NOTHING_TO_INLINE", "unused")
2 |
3 | package com.xzy.utils.toast
4 |
5 | import android.annotation.SuppressLint
6 | import android.content.Context
7 | import android.content.ContextWrapper
8 | import android.os.Build
9 | import android.util.Log
10 | import android.view.View
11 | import android.view.ViewGroup
12 | import android.view.WindowManager
13 | import android.widget.Toast
14 | import androidx.annotation.StringRes
15 | import androidx.fragment.app.Fragment
16 | import com.xzy.utils.appctx.appCtx
17 | import com.xzy.utils.os.layoutInflater
18 | import com.xzy.utils.os.osVerCode
19 | import com.xzy.utils.os.windowManager
20 | import com.xzy.utils.resource.txt
21 | import kotlin.LazyThreadSafetyMode.NONE
22 |
23 | @PublishedApi
24 | internal fun Context.createToast(text: CharSequence, duration: Int): Toast {
25 | val ctx = if (osVerCode == Build.VERSION_CODES.N_MR1) SafeToastCtx(this) else this
26 | return Toast.makeText(ctx, text, duration)
27 | }
28 |
29 | @PublishedApi
30 | internal fun Context.createToast(@StringRes resId: Int, duration: Int): Toast {
31 | return createToast(txt(resId), duration)
32 | }
33 |
34 | inline fun Context.toast(@StringRes msgResId: Int) =
35 | createToast(msgResId, Toast.LENGTH_SHORT).show()
36 |
37 | inline fun Fragment.toast(@StringRes msgResId: Int) = ctx.toast(msgResId)
38 |
39 | inline fun View.toast(@StringRes msgResId: Int) = context.toast(msgResId)
40 |
41 | inline fun toast(@StringRes msgResId: Int) = appCtx.toast(msgResId)
42 |
43 | inline fun Context.toast(msg: CharSequence) = createToast(msg, Toast.LENGTH_SHORT).show()
44 |
45 | inline fun Fragment.toast(msg: CharSequence) = ctx.toast(msg)
46 |
47 | inline fun View.toast(msg: CharSequence) = context.toast(msg)
48 |
49 | inline fun toast(msg: CharSequence) = appCtx.toast(msg)
50 |
51 | inline fun Context.longToast(@StringRes msgResId: Int) =
52 | createToast(msgResId, Toast.LENGTH_LONG).show()
53 |
54 | inline fun Fragment.longToast(@StringRes msgResId: Int) = ctx.longToast(msgResId)
55 |
56 | inline fun View.longToast(@StringRes msgResId: Int) = context.longToast(msgResId)
57 |
58 | inline fun longToast(@StringRes msgResId: Int) = appCtx.longToast(msgResId)
59 |
60 | inline fun Context.longToast(msg: CharSequence) = createToast(msg, Toast.LENGTH_LONG).show()
61 |
62 | inline fun Fragment.longToast(msg: CharSequence) = ctx.longToast(msg)
63 |
64 | inline fun View.longToast(msg: CharSequence) = context.longToast(msg)
65 |
66 | inline fun longToast(msg: CharSequence) = appCtx.longToast(msg)
67 |
68 | @PublishedApi
69 | internal inline val Fragment.ctx: Context
70 | get() = context ?: appCtx
71 |
72 | /**
73 | * Avoids [WindowManager.BadTokenException] on API 25.
74 | */
75 | private class SafeToastCtx(ctx: Context) : ContextWrapper(ctx) {
76 |
77 | private val toastWindowManager by lazy(NONE) { ToastWindowManager(baseContext.windowManager) }
78 |
79 | private val toastLayoutInflater by lazy(NONE) {
80 | baseContext.layoutInflater.cloneInContext(this)
81 | }
82 |
83 | override fun getApplicationContext(): Context = SafeToastCtx(baseContext.applicationContext)
84 |
85 | override fun getSystemService(name: String): Any = when (name) {
86 | Context.LAYOUT_INFLATER_SERVICE -> toastLayoutInflater
87 | Context.WINDOW_SERVICE -> toastWindowManager
88 | else -> super.getSystemService(name)
89 | }
90 |
91 | private class ToastWindowManager(private val base: WindowManager) : WindowManager by base {
92 |
93 | @SuppressLint("LogNotTimber") // Timber is not a dependency here, but lint passes through.
94 | override fun addView(view: View?, params: ViewGroup.LayoutParams?) {
95 | try {
96 | base.addView(view, params)
97 | } catch (e: WindowManager.BadTokenException) {
98 | Log.e("SafeToast", "Couldn't add Toast to WindowManager", e)
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/vibrate/VibrateUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.vibrate;
2 |
3 | import android.content.Context;
4 | import android.os.Vibrator;
5 |
6 | import androidx.annotation.RequiresPermission;
7 |
8 | import com.xzy.utils.common.Utils;
9 |
10 | import static android.Manifest.permission.VIBRATE;
11 |
12 | /**
13 | * 震动相关的工具类。
14 | * 参考 https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/
15 | * java/com/blankj/utilcode/util/VibrateUtils.java
16 | *
17 | * @author xzy
18 | */
19 | @SuppressWarnings("all")
20 | public final class VibrateUtils {
21 |
22 | private static Vibrator vibrator;
23 |
24 | private VibrateUtils() {
25 | throw new UnsupportedOperationException("u can't instantiate me...");
26 | }
27 |
28 | /**
29 | * Vibrate.
30 | * Must hold {@code }
31 | *
32 | * @param milliseconds The number of milliseconds to vibrate.
33 | */
34 | @RequiresPermission(VIBRATE)
35 | public static void vibrate(final long milliseconds) {
36 | Vibrator vibrator = getVibrator();
37 | if (vibrator == null) return;
38 | vibrator.vibrate(milliseconds);
39 | }
40 |
41 | /**
42 | * Vibrate.
43 | * Must hold {@code }
44 | *
45 | * @param pattern An array of longs of times for which to turn the vibrator on or off.
46 | * @param repeat The index into pattern at which to repeat, or -1 if you don't want to repeat.
47 | */
48 | @RequiresPermission(VIBRATE)
49 | public static void vibrate(final long[] pattern, final int repeat) {
50 | Vibrator vibrator = getVibrator();
51 | if (vibrator == null) return;
52 | vibrator.vibrate(pattern, repeat);
53 | }
54 |
55 | /**
56 | * Cancel vibrate.
57 | * Must hold {@code }
58 | */
59 | @RequiresPermission(VIBRATE)
60 | public static void cancel() {
61 | Vibrator vibrator = getVibrator();
62 | if (vibrator == null) return;
63 | vibrator.cancel();
64 | }
65 |
66 | private static Vibrator getVibrator() {
67 | if (vibrator == null) {
68 | vibrator = (Vibrator) Utils.getApp().getSystemService(Context.VIBRATOR_SERVICE);
69 | }
70 | return vibrator;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib_util/src/main/java/com/xzy/utils/view/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils.view;
2 |
3 | import android.os.Build;
4 | import android.text.TextUtils;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import com.xzy.utils.common.Utils;
9 |
10 | import java.util.Locale;
11 |
12 | /**
13 | * View 相关的工具类。
14 | * 参考 https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/
15 | * java/com/blankj/utilcode/util/ViewUtils.java
16 | *
17 | * @author xzy
18 | */
19 | @SuppressWarnings("all")
20 | public class ViewUtils {
21 |
22 | /**
23 | * Set the enabled state of this view.
24 | *
25 | * @param view The view.
26 | * @param enabled True to enabled, false otherwise.
27 | */
28 | public static void setViewEnabled(View view, boolean enabled) {
29 | setViewEnabled(view, enabled, (View) null);
30 | }
31 |
32 | /**
33 | * Set the enabled state of this view.
34 | *
35 | * @param view The view.
36 | * @param enabled True to enabled, false otherwise.
37 | * @param excludes The excludes.
38 | */
39 | public static void setViewEnabled(View view, boolean enabled, View... excludes) {
40 | if (view == null) return;
41 | if (excludes != null) {
42 | for (View exclude : excludes) {
43 | if (view == exclude) return;
44 | }
45 | }
46 | if (view instanceof ViewGroup) {
47 | ViewGroup viewGroup = (ViewGroup) view;
48 | int childCount = viewGroup.getChildCount();
49 | for (int i = 0; i < childCount; i++) {
50 | setViewEnabled(viewGroup.getChildAt(i), enabled, excludes);
51 | }
52 | }
53 | view.setEnabled(enabled);
54 | }
55 |
56 | /**
57 | * @param runnable The runnable
58 | */
59 | public static void runOnUiThread(final Runnable runnable) {
60 | Utils.runOnUiThread(runnable);
61 | }
62 |
63 | /**
64 | * @param runnable The runnable.
65 | * @param delayMillis The delay (in milliseconds) until the Runnable will be executed.
66 | */
67 | public static void runOnUiThreadDelayed(final Runnable runnable, long delayMillis) {
68 | Utils.runOnUiThreadDelayed(runnable, delayMillis);
69 | }
70 |
71 | /**
72 | * Return whether horizontal layout direction of views are from Right to Left.
73 | *
74 | * @return {@code true}: yes
{@code false}: no
75 | */
76 | public static boolean isLayoutRtl() {
77 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
78 | Locale primaryLocale;
79 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
80 | primaryLocale = Utils.getApp().getResources().getConfiguration().getLocales().get(0);
81 | } else {
82 | primaryLocale = Utils.getApp().getResources().getConfiguration().locale;
83 | }
84 | return TextUtils.getLayoutDirectionFromLocale(primaryLocale) == View.LAYOUT_DIRECTION_RTL;
85 | }
86 | return false;
87 | }
88 |
89 | /**
90 | * 用于解决ScrollView嵌套ListView/GridView/WebView/RecyclerView等无法置顶问题
91 | *
92 | * @param view ScrollView嵌套的跟视图
93 | */
94 | public static void fixScrollViewTopping(View view) {
95 | view.setFocusable(false);
96 | ViewGroup viewGroup = null;
97 | if (view instanceof ViewGroup) {
98 | viewGroup = (ViewGroup) view;
99 | }
100 | if (viewGroup == null) {
101 | return;
102 | }
103 | for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) {
104 | View childAt = viewGroup.getChildAt(i);
105 | childAt.setFocusable(false);
106 | if (childAt instanceof ViewGroup) {
107 | fixScrollViewTopping(childAt);
108 | }
109 | }
110 | }
111 |
112 | }
--------------------------------------------------------------------------------
/lib_util/src/main/res/anim/in_from_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/anim/in_from_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/anim/out_to_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/anim/out_to_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_excel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_excel.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_mp4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_mp4.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_music.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_pdf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_pdf.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_ppt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_ppt.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_txt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_txt.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_video.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_word.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_word.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-mdpi/icon_file_zip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-mdpi/icon_file_zip.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hgncxzy/AndroidUtils/7e0ea98ebf5521c7ae2c1320167e570af2593dd2/lib_util/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/lib_util/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | utils
3 |
4 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib_util/src/main/res/xml/provider_path.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib_util/src/test/java/com/xzy/utils/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.xzy.utils;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':lib_util'
2 |
--------------------------------------------------------------------------------