提交 c0dd366b 编写于 作者: B Blankj

see 08/08 log

上级 632a5a92
* `19/08/08` [add] BusUtils#post tag support one-to-many. Publish v1.25.6.
* `19/08/04` [add] ThreadUtils#Task support timeout.
* `19/08/01` [upd] EncryptUtils#rsa.
* `19/07/31` [add] DeviceUtils#getUniqueDeviceId, DeviceUtils#isSameDevice. Publish v1.25.5.
* `19/07/30` [fix] ThreadUtils's task can only be executed once. PhoneUtils#getIMEI wrong.
* `19/07/29` [fix] BusUtils post father class useless. KeyboardUtils#hideSoft bug. Publish v1.25.4.
......
......@@ -45,7 +45,7 @@
[frame]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png
[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.5-brightgreen.svg
[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.6-brightgreen.svg
[auc]: https://github.com/Blankj/AndroidUtilCode
[apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg
......
......@@ -45,7 +45,7 @@ If this project helps you a lot and you want to support the project's developmen
[frame]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png
[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.5-brightgreen.svg
[aucSvg]: https://img.shields.io/badge/AndroidUtilCode-v1.25.6-brightgreen.svg
[auc]: https://github.com/Blankj/AndroidUtilCode
[apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg
......
......@@ -29,6 +29,7 @@ android {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
applicationIdSuffix ".debug"
}
release {
minifyEnabled true
......
......@@ -14,8 +14,8 @@ class Config {
static compileSdkVersion = 28
static minSdkVersion = 14
static targetSdkVersion = 28
static versionCode = 1_025_005
static versionName = '1.25.5'// E.g. 1.9.72 => 1,009,072
static versionCode = 1_025_006
static versionName = '1.25.6'// E.g. 1.9.72 => 1,009,072
// lib version
static kotlin_version = '1.3.10'
......@@ -29,7 +29,7 @@ class Config {
static depConfig = [
plugin : [
gradle : new DepConfig("com.android.tools.build:gradle:3.3.0"),
gradle : new DepConfig("com.android.tools.build:gradle:3.4.0"),
kotlin : new DepConfig("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"),
maven : new DepConfig("com.github.dcendents:android-maven-gradle-plugin:2.1"),// 上传到 maven
bintray: new DepConfig("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4"),// 上传到 bintray
......@@ -37,9 +37,9 @@ class Config {
// 本地第一次上传插件新的版本需设置 useLocal = true, isApply = false
// 本地上传成功之后 isApply = true 即可应用插件来调试,后续版本更新无需设置 isApply = false
// 发布版本的话把 useLocal = false, isApply = false,发布成功后 isApply = true 即可使用远程库版本
// 发布版本的话把 useLocal = false, isApply = false,更新版本号,发布成功后 isApply = true 即可使用远程库版本
api : new DepConfig(false/*是否本地调试*/, "com.blankj:api-gradle-plugin:1.0", true/*是否使用插件*/),
bus : new DepConfig(false/*是否本地调试*/, "com.blankj:bus-gradle-plugin:2.0", true/*是否使用插件*/),
bus : new DepConfig(false/*是否本地调试*/, "com.blankj:bus-gradle-plugin:2.1", true/*是否使用插件*/),
],
api_gradle_plugin: new DepConfig(":plugin:api-gradle-plugin", false),
......
......@@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" /><!--installAppSilent-->
<uses-permission android:name="android.permission.DELETE_PACKAGES" /><!--uninstallAppSilent-->
......
......@@ -24,7 +24,7 @@ class DangerousActivity : CommonTitleActivity() {
companion object {
fun start(context: Context) {
PermissionHelper.requestStorage(object : PermissionHelper.OnPermissionGrantedListener {
PermissionHelper.requestStorageAndSms(object : PermissionHelper.OnPermissionGrantedListener {
override fun onPermissionGranted() {
val starter = Intent(context, DangerousActivity::class.java)
context.startActivity(starter)
......@@ -64,7 +64,8 @@ class DangerousActivity : CommonTitleActivity() {
dangerousShutdownBtn,
dangerousRebootBtn,
dangerousReboot2RecoveryBtn,
dangerousReboot2BootloaderBtn
dangerousReboot2BootloaderBtn,
dangerousSendSmsSilentBtn
)
if (AppUtils.isAppSystem()) {
......@@ -111,6 +112,8 @@ class DangerousActivity : CommonTitleActivity() {
R.id.dangerousRebootBtn -> DangerousUtils.reboot()
R.id.dangerousReboot2RecoveryBtn -> DangerousUtils.reboot2Recovery()
R.id.dangerousReboot2BootloaderBtn -> DangerousUtils.reboot2Bootloader()
R.id.dangerousReboot2BootloaderBtn -> DangerousUtils.reboot2Bootloader()
R.id.dangerousSendSmsSilentBtn -> DangerousUtils.sendSmsSilent("10000", "sendSmsSilent")
}
}
}
......
......@@ -14,9 +14,9 @@ import com.blankj.utilcode.util.PermissionUtils
*/
object PermissionHelper {
fun requestStorage(listener: OnPermissionGrantedListener,
deniedListener: OnPermissionDeniedListener) {
request(listener, deniedListener, PermissionConstants.STORAGE)
fun requestStorageAndSms(listener: OnPermissionGrantedListener,
deniedListener: OnPermissionDeniedListener) {
request(listener, deniedListener, PermissionConstants.STORAGE, PermissionConstants.SMS)
}
fun requestLocation(listener: OnPermissionGrantedListener,
......
......@@ -55,11 +55,18 @@
android:layout_height="wrap_content"
android:text="@string/dangerous_reboot_to_bootloader" />
<Button
android:id="@+id/dangerousSendSmsSilentBtn"
style="@style/WideBtnStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dangerous_reboot_to_bootloader" />
<CheckBox
android:id="@+id/dangerousMobileDataEnabledCb"
style="@style/CbStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dangerous_data_enabled" />
android:text="@string/dangerous_send_sms_silent" />
</LinearLayout>
......@@ -21,5 +21,6 @@
<string name="dangerous_reboot">Reboot</string>
<string name="dangerous_reboot_to_recovery">Reboot To Recovery</string>
<string name="dangerous_reboot_to_bootloader">Reboot To Bootloader</string>
<string name="dangerous_send_sms_silent">Send SMS Silent</string>
<string name="dangerous_data_enabled">Mobile Data Enabled</string>
</resources>
\ No newline at end of file
......@@ -239,8 +239,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<!-- process -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
......
package com.blankj.utilcode.pkg.feature.bus
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
......@@ -32,6 +33,14 @@ class BusActivity : CommonTitleActivity() {
busAboutTv.text = param
}
@SuppressLint("SetTextI18n")
@BusUtils.Bus(tag = TAG_BUS, priority = 1)
fun testSameTag(param: String) {
if (busAboutTv.text.toString().equals(TAG_BUS)) {
busAboutTv.text = "${busAboutTv.text} * 2"
}
}
@BusUtils.Bus(tag = TAG_STICKY_BUS, sticky = true)
fun testSticky(callback: Callback?) {
busAboutTv.text = callback?.call()
......
......@@ -3,7 +3,10 @@ package com.blankj.utilcode.pkg.feature.keyboard
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import com.blankj.common.CommonTitleActivity
import com.blankj.utilcode.pkg.R
import com.blankj.utilcode.pkg.helper.DialogHelper
......@@ -82,9 +85,7 @@ class KeyboardActivity : CommonTitleActivity() {
// if (ev.action == MotionEvent.ACTION_DOWN) {
// val v = currentFocus
// if (isShouldHideKeyboard(v, ev)) {
// val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
// ?: return super.dispatchTouchEvent(ev)
// imm.hideSoftInputFromWindow(v.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
// KeyboardUtils.hideSoftInput(this);
// }
// }
// return super.dispatchTouchEvent(ev)
......
......@@ -23,7 +23,7 @@ class PhoneActivity : CommonTitleActivity() {
companion object {
fun start(context: Context) {
PermissionHelper.requestPhoneAndSms(object : PermissionHelper.OnPermissionGrantedListener {
PermissionHelper.requestPhone(object : PermissionHelper.OnPermissionGrantedListener {
override fun onPermissionGranted() {
val starter = Intent(context, PhoneActivity::class.java)
context.startActivity(starter)
......@@ -64,8 +64,7 @@ class PhoneActivity : CommonTitleActivity() {
applyDebouncingClickListener(
phoneDialBtn,
phoneCallBtn,
phoneSendSmsBtn,
phoneSendSmsSilentBtn
phoneSendSmsBtn
)
}
......@@ -76,7 +75,6 @@ class PhoneActivity : CommonTitleActivity() {
R.id.phoneDialBtn -> PhoneUtils.dial("10000")
R.id.phoneCallBtn -> PhoneUtils.call("10000")
R.id.phoneSendSmsBtn -> PhoneUtils.sendSms("10000", "sendSms")
R.id.phoneSendSmsSilentBtn -> PhoneUtils.sendSmsSilent("10000", "sendSmsSilent")
}
}
}
......@@ -24,9 +24,9 @@ object PermissionHelper {
request(listener, deniedListener, PermissionConstants.STORAGE)
}
fun requestPhoneAndSms(listener: OnPermissionGrantedListener,
deniedListener: OnPermissionDeniedListener) {
request(listener, deniedListener, PermissionConstants.PHONE, PermissionConstants.SMS)
fun requestPhone(listener: OnPermissionGrantedListener,
deniedListener: OnPermissionDeniedListener) {
request(listener, deniedListener, PermissionConstants.PHONE)
}
private fun request(grantedListener: OnPermissionGrantedListener,
......
......@@ -33,11 +33,4 @@
android:layout_height="wrap_content"
android:text="@string/phone_send_sms" />
<Button
android:id="@+id/phoneSendSmsSilentBtn"
style="@style/WideBtnStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/phone_send_sms_silent" />
</LinearLayout>
......@@ -261,7 +261,6 @@
<string name="phone_dial">Dial</string>
<string name="phone_call">Call</string>
<string name="phone_send_sms">Send SMS</string>
<string name="phone_send_sms_silent">Send SMS Silent</string>
<!--Process 相关-->
<string name="process_kill_all_background">Kill All Background Processes</string>
......
......@@ -18,4 +18,7 @@ org.gradle.daemon=true
#org.gradle.configureondemand=true
#org.gradle.parallel=true
#-Dorg.gradle.debug=true --no-daemon
\ No newline at end of file
#-Dorg.gradle.debug=true --no-daemon
gradle.publish.key=MJwR6d1wZZRgwtOpVAKIrwbJwEfOHZeT
gradle.publish.secret=quROE9JGWjAY8UP3wyEOuPZ5qtmwjG2w
\ No newline at end of file
......@@ -30,6 +30,7 @@ public abstract class CommonBackActivity extends BaseActivity {
final SwipePanel swipeLayout = new SwipePanel(this);
swipeLayout.setLeftDrawable(R.drawable.base_back);
swipeLayout.setLeftEdgeSize(SizeUtils.dp2px(100));
swipeLayout.setLeftSwipeColor(getResources().getColor(R.color.colorPrimary));
swipeLayout.wrapView(findViewById(android.R.id.content));
swipeLayout.setOnFullSwipeListener(new SwipePanel.OnFullSwipeListener() {
@Override
......
package com.blankj.subutil.util;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.RequiresPermission;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import com.blankj.utilcode.util.ShellUtils;
......@@ -13,8 +16,10 @@ import com.blankj.utilcode.util.Utils;
import java.io.File;
import java.lang.reflect.Method;
import java.util.List;
import static android.Manifest.permission.MODIFY_PHONE_STATE;
import static android.Manifest.permission.SEND_SMS;
/**
* <pre>
......@@ -300,4 +305,26 @@ public class DangerousUtils {
}
return false;
}
/**
* Send sms silently.
* <p>Must hold {@code <uses-permission android:name="android.permission.SEND_SMS" />}</p>
*
* @param phoneNumber The phone number.
* @param content The content.
*/
@RequiresPermission(SEND_SMS)
public static void sendSmsSilent(final String phoneNumber, final String content) {
if (TextUtils.isEmpty(content)) return;
PendingIntent sentIntent = PendingIntent.getBroadcast(Utils.getApp(), 0, new Intent("send"), 0);
SmsManager smsManager = SmsManager.getDefault();
if (content.length() >= 70) {
List<String> ms = smsManager.divideMessage(content);
for (String str : ms) {
smsManager.sendTextMessage(phoneNumber, null, str, sentIntent, null);
}
} else {
smsManager.sendTextMessage(phoneNumber, null, content, sentIntent, null);
}
}
}
......@@ -2,10 +2,10 @@
Gradle:
```groovy
implementation 'com.blankj:utilcode:1.25.5'
implementation 'com.blankj:utilcode:1.25.6'
// if u use AndroidX, use the following
implementation 'com.blankj:utilcodex:1.25.5'
implementation 'com.blankj:utilcodex:1.25.6'
```
......
......@@ -2,10 +2,10 @@
Gradle:
```groovy
implementation 'com.blankj:utilcode:1.25.5'
implementation 'com.blankj:utilcode:1.25.6'
// if u use AndroidX, use the following
implementation 'com.blankj:utilcodex:1.25.5'
implementation 'com.blankj:utilcodex:1.25.6'
```
......
......@@ -31,7 +31,7 @@ public final class BusUtils {
private static final Object NULL = "nULl";
private static final String TAG = "BusUtils";
private final Map<String, BusInfo> mTag_BusInfoMap = new HashMap<>();
private final Map<String, List<BusInfo>> mTag_BusInfoListMap = new HashMap<>();
private final Map<String, Set<Object>> mClassName_BusesMap = new ConcurrentHashMap<>();
private final Map<String, List<String>> mClassName_TagsMap = new HashMap<>();
......@@ -50,7 +50,18 @@ public final class BusUtils {
private void registerBus(String tag,
String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode) {
mTag_BusInfoMap.put(tag, new BusInfo(className, funName, paramType, paramName, sticky, threadMode));
registerBus(tag, className, funName, paramType, paramName, sticky, threadMode, 0);
}
private void registerBus(String tag,
String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode, int priority) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
busInfoList = new ArrayList<>();
mTag_BusInfoListMap.put(tag, busInfoList);
}
busInfoList.add(new BusInfo(className, funName, paramType, paramName, sticky, threadMode, priority));
}
public static void register(final Object bus) {
......@@ -87,7 +98,7 @@ public final class BusUtils {
@Override
public String toString() {
return "BusUtils: " + mTag_BusInfoMap;
return "BusUtils: " + mTag_BusInfoListMap;
}
private static BusUtils getInstance() {
......@@ -112,15 +123,16 @@ public final class BusUtils {
tags = mClassName_TagsMap.get(className);
if (tags == null) {
tags = new ArrayList<>();
for (Map.Entry<String, BusInfo> entry : mTag_BusInfoMap.entrySet()) {
BusInfo value = entry.getValue();
try {
if (Class.forName(value.className).isAssignableFrom(aClass)) {
tags.add(entry.getKey());
value.classNames.add(className);
for (Map.Entry<String, List<BusInfo>> entry : mTag_BusInfoListMap.entrySet()) {
for (BusInfo busInfo : entry.getValue()) {
try {
if (Class.forName(busInfo.className).isAssignableFrom(aClass)) {
tags.add(entry.getKey());
busInfo.classNames.add(className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
mClassName_TagsMap.put(className, tags);
......@@ -158,19 +170,21 @@ public final class BusUtils {
}
private void postInner(final String tag, final Object arg, final boolean sticky) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
return;
}
if (busInfo.method == null) {
Method method = getMethodByBusInfo(busInfo);
if (method == null) {
return;
for (BusInfo busInfo : busInfoList) {
if (busInfo.method == null) {
Method method = getMethodByBusInfo(busInfo);
if (method == null) {
return;
}
busInfo.method = method;
}
busInfo.method = method;
invokeMethod(tag, arg, busInfo, sticky);
}
invokeMethod(tag, arg, busInfo, sticky);
}
private Method getMethodByBusInfo(BusInfo busInfo) {
......@@ -273,43 +287,47 @@ public final class BusUtils {
}
private void postStickyInner(final String tag, final Object arg) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
return;
}
if (!busInfo.sticky) { // not sticky bus will post directly.
postInner(tag, arg);
return;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null) {
tagArgMap = new HashMap<>();
mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);
for (BusInfo busInfo : busInfoList) {
if (!busInfo.sticky) { // not sticky bus will post directly.
postInner(tag, arg);
return;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null) {
tagArgMap = new HashMap<>();
mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);
}
tagArgMap.put(tag, arg);
}
tagArgMap.put(tag, arg);
postInner(tag, arg, true);
}
postInner(tag, arg, true);
}
private void removeStickyInner(final String tag) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
return;
}
if (!busInfo.sticky) {
Log.e(TAG, "The bus of tag <" + tag + "> is not sticky.");
return;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null || !tagArgMap.containsKey(tag)) {
Log.e(TAG, "The sticky bus of tag <" + tag + "> didn't post.");
for (BusInfo busInfo : busInfoList) {
if (!busInfo.sticky) {
Log.e(TAG, "The bus of tag <" + tag + "> is not sticky.");
return;
}
tagArgMap.remove(tag);
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null || !tagArgMap.containsKey(tag)) {
Log.e(TAG, "The sticky bus of tag <" + tag + "> didn't post.");
return;
}
tagArgMap.remove(tag);
}
}
}
......@@ -321,17 +339,19 @@ public final class BusUtils {
String paramName;
boolean sticky;
String threadMode;
int priority;
Method method;
List<String> classNames;
BusInfo(String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode) {
boolean sticky, String threadMode, int priority) {
this.className = className;
this.funName = funName;
this.paramType = paramType;
this.paramName = paramName;
this.sticky = sticky;
this.threadMode = threadMode;
this.priority = priority;
this.classNames = new CopyOnWriteArrayList<>();
}
......@@ -342,6 +362,7 @@ public final class BusUtils {
", sticky: " + sticky +
", threadMode: " + threadMode +
", method: " + method +
", priority: " + priority +
" }";
}
}
......@@ -358,6 +379,8 @@ public final class BusUtils {
boolean sticky() default false;
ThreadMode threadMode() default ThreadMode.POSTING;
int priority() default 0;
}
private static class LazyHolder {
......
......@@ -975,96 +975,96 @@ public final class EncryptUtils {
* Return the Base64-encode bytes of RSA encryption.
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param publicKey The public key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.
* @return the Base64-encode bytes of RSA encryption
*/
public static byte[] encryptRSA2Base64(final byte[] data,
final byte[] key,
final boolean isPublicKey,
final byte[] publicKey,
final int keySize,
final String transformation) {
return base64Encode(encryptRSA(data, key, isPublicKey, transformation));
return base64Encode(encryptRSA(data, publicKey, keySize, transformation));
}
/**
* Return the hex string of RSA encryption.
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param publicKey The public key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.
* @return the hex string of RSA encryption
*/
public static String encryptRSA2HexString(final byte[] data,
final byte[] key,
final boolean isPublicKey,
final byte[] publicKey,
final int keySize,
final String transformation) {
return bytes2HexString(encryptRSA(data, key, isPublicKey, transformation));
return bytes2HexString(encryptRSA(data, publicKey, keySize, transformation));
}
/**
* Return the bytes of RSA encryption.
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param publicKey The public key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.
* @return the bytes of RSA encryption
*/
public static byte[] encryptRSA(final byte[] data,
final byte[] key,
final boolean isPublicKey,
final byte[] publicKey,
final int keySize,
final String transformation) {
return rsaTemplate(data, key, isPublicKey, transformation, true);
return rsaTemplate(data, publicKey, keySize, transformation, true);
}
/**
* Return the bytes of RSA decryption for Base64-encode bytes.
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param privateKey The private key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.
* @return the bytes of RSA decryption for Base64-encode bytes
*/
public static byte[] decryptBase64RSA(final byte[] data,
final byte[] key,
final boolean isPublicKey,
final byte[] privateKey,
final int keySize,
final String transformation) {
return decryptRSA(base64Decode(data), key, isPublicKey, transformation);
return decryptRSA(base64Decode(data), privateKey, keySize, transformation);
}
/**
* Return the bytes of RSA decryption for hex string.
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param privateKey The private key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.
* @return the bytes of RSA decryption for hex string
*/
public static byte[] decryptHexStringRSA(final String data,
final byte[] key,
final boolean isPublicKey,
final byte[] privateKey,
final int keySize,
final String transformation) {
return decryptRSA(hexString2Bytes(data), key, isPublicKey, transformation);
return decryptRSA(hexString2Bytes(data), privateKey, keySize, transformation);
}
/**
* Return the bytes of RSA decryption.
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param privateKey The private key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.
* @return the bytes of RSA decryption
*/
public static byte[] decryptRSA(final byte[] data,
final byte[] key,
final boolean isPublicKey,
final byte[] privateKey,
final int keySize,
final String transformation) {
return rsaTemplate(data, key, isPublicKey, transformation, false);
return rsaTemplate(data, privateKey, keySize, transformation, false);
}
/**
......@@ -1072,14 +1072,14 @@ public final class EncryptUtils {
*
* @param data The data.
* @param key The key.
* @param isPublicKey True to use public key, false to use private key.
* @param keySize The size of key, e.g. 1024, 2048...
* @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS1Padding</i>.
* @param isEncrypt True to encrypt, false otherwise.
* @return the bytes of RSA encryption or decryption
*/
private static byte[] rsaTemplate(final byte[] data,
final byte[] key,
final boolean isPublicKey,
final int keySize,
final String transformation,
final boolean isEncrypt) {
if (data == null || data.length == 0 || key == null || key.length == 0) {
......@@ -1087,7 +1087,7 @@ public final class EncryptUtils {
}
try {
Key rsaKey;
if (isPublicKey) {
if (isEncrypt) {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);
rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
} else {
......@@ -1098,7 +1098,13 @@ public final class EncryptUtils {
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, rsaKey);
int len = data.length;
int maxLen = isEncrypt ? 117 : 128;
int maxLen = keySize / 8;
if (isEncrypt) {
String lowerTrans = transformation.toLowerCase();
if (lowerTrans.endsWith("pkcs1padding")) {
maxLen -= 11;
}
}
int count = len / maxLen;
if (count > 0) {
byte[] ret = new byte[0];
......
......@@ -279,11 +279,7 @@ public final class KeyboardUtils {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (isShouldHideKeyboard(v, ev)) {
InputMethodManager imm =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS
);
KeyboardUtils.hideSoftInput(this);
}
}
return super.dispatchTouchEvent(ev);
......
......@@ -359,28 +359,6 @@ public final class PhoneUtils {
return false;
}
/**
* Send sms silently.
* <p>Must hold {@code <uses-permission android:name="android.permission.SEND_SMS" />}</p>
*
* @param phoneNumber The phone number.
* @param content The content.
*/
@RequiresPermission(SEND_SMS)
public static void sendSmsSilent(final String phoneNumber, final String content) {
if (TextUtils.isEmpty(content)) return;
PendingIntent sentIntent = PendingIntent.getBroadcast(Utils.getApp(), 0, new Intent("send"), 0);
SmsManager smsManager = SmsManager.getDefault();
if (content.length() >= 70) {
List<String> ms = smsManager.divideMessage(content);
for (String str : ms) {
smsManager.sendTextMessage(phoneNumber, null, str, sentIntent, null);
}
} else {
smsManager.sendTextMessage(phoneNumber, null, content, sentIntent, null);
}
}
private static TelephonyManager getTelephonyManager() {
return (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);
}
......
......@@ -2,6 +2,7 @@ package com.blankj.utilcode.util;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.CallSuper;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.util.Log;
......@@ -1155,12 +1156,15 @@ public final class ThreadUtils {
private static final int COMPLETING = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTED = 5;
private static final int TIMEOUT = 6;
private final AtomicInteger state = new AtomicInteger(NEW);
private volatile boolean isSchedule;
private volatile Thread runner;
private Timer mTimer;
private Executor deliver;
public abstract T doInBackground() throws Throwable;
......@@ -1200,19 +1204,19 @@ public final class ThreadUtils {
@Override
public void run() {
onSuccess(result);
removeTask(Task.this);
onDone();
}
});
}
} catch (InterruptedException ignore) {
state.set(INTERRUPTED);
state.compareAndSet(CANCELLED, INTERRUPTED);
} catch (final Throwable throwable) {
if (!state.compareAndSet(RUNNING, EXCEPTIONAL)) return;
getDeliver().execute(new Runnable() {
@Override
public void run() {
onFail(throwable);
removeTask(Task.this);
onDone();
}
});
}
......@@ -1237,11 +1241,23 @@ public final class ThreadUtils {
@Override
public void run() {
onCancel();
removeTask(Task.this);
onDone();
}
});
}
private void timeout() {
synchronized (state) {
if (state.get() > RUNNING) return;
state.set(TIMEOUT);
}
if (runner != null) {
runner.interrupt();
}
onDone();
}
public boolean isCanceled() {
return state.get() >= CANCELLED;
}
......@@ -1255,6 +1271,19 @@ public final class ThreadUtils {
return this;
}
public void setTimeout(final long timeoutMillis, final OnTimeoutListener listener) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
if (!isDone() && listener != null) {
timeout();
listener.onTimeout();
}
}
}, timeoutMillis);
}
private void setSchedule(boolean isSchedule) {
this.isSchedule = isSchedule;
}
......@@ -1265,6 +1294,19 @@ public final class ThreadUtils {
}
return deliver;
}
@CallSuper
protected void onDone() {
TASK_TASKINFO_MAP.remove(this);
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
public interface OnTimeoutListener {
void onTimeout();
}
}
private static Executor getGlobalDeliver() {
......@@ -1281,10 +1323,6 @@ public final class ThreadUtils {
return sDeliver;
}
private static void removeTask(final Task task) {
TASK_TASKINFO_MAP.remove(task);
}
private static class TaskInfo {
private TimerTask mTimerTask;
private ExecutorService mService;
......
......@@ -38,8 +38,6 @@ public class BaseTest {
}
});
Utils.init(RuntimeEnvironment.application);
ReflectUtils getInstance = ReflectUtils.reflect(BusUtils.class).method("getInstance");
getInstance.method("registerBus", "base", BaseTest.class.getName(), "noParamFun", "int", "i", false, "POSTING");
}
@Test
......
......@@ -3,7 +3,12 @@ package com.blankj.utilcode.util;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* <pre>
......@@ -30,6 +35,11 @@ public class BusUtilsTest extends BaseTest {
System.out.println("noParam");
}
@BusUtils.Bus(tag = TAG_NO_PARAM)
public void noParamSameTagFun() {
System.out.println("sameTag: noParam");
}
@BusUtils.Bus(tag = TAG_ONE_PARAM)
public void oneParamFun(String param) {
System.out.println(param);
......@@ -96,6 +106,8 @@ public class BusUtilsTest extends BaseTest {
// }
// }).start();
// }
// CountDownLatch countDownLatch = new CountDownLatch(1);
// BusUtils.register(test);
// for (int i = 0; i < 100; i++) {
// new Thread(new Runnable() {
// @Override
......@@ -104,6 +116,12 @@ public class BusUtilsTest extends BaseTest {
// }
// }).start();
// }
// try {
// countDownLatch.await(1, TimeUnit.SECONDS);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// BusUtils.unregister(test);
// for (int i = 0; i < 100; i++) {
// new Thread(new Runnable() {
// @Override
......@@ -241,12 +259,25 @@ public class BusUtilsTest extends BaseTest {
@Test
public void testBase() {
ReflectUtils getInstance = ReflectUtils.reflect(BusUtils.class).method("getInstance");
getInstance.method("registerBus", "base", BaseTest.class.getName(), "noParamFun", "int", "i", false, "POSTING");
BaseTest t = new BusUtilsTest();
BusUtils.register(t);
BusUtils.post("base", 1);
BusUtils.unregister(t);
}
@Test
public void testSameTag() {
ReflectUtils.reflect(BusUtils.class).method("getInstance")
.method("registerBus", TAG_NO_PARAM, BusUtilsTest.class.getName(), "noParamSameTagFun", "", "", false, "POSTING", 2);
BusUtilsTest test = new BusUtilsTest();
BusUtils.register(test);
BusUtils.post(TAG_NO_PARAM);
BusUtils.unregister(test);
}
public interface Callback {
String call();
}
......
package com.blankj.utilcode.util;
import android.util.Base64;
import android.util.Pair;
import org.junit.Assert;
import org.junit.Test;
......@@ -376,27 +377,34 @@ public class EncryptUtilsTest extends BaseTest {
);
}
private String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCWuAuSCrzUXC1l4ixXBeBfotUtkALrAjLM5UHiVfOFHrRJHM41HSeHVm56UZHgJlwk80R8juu1ykuhkgrilTv7H+3MpZdIunvndDElgdgk8aI2Ip4GUlemUDvCtWd3ychWEh4kYQ8CeInQvNM08imoLFldvbjWt/IkGK+BcGzamQIDAQAB";
private String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJa4C5IKvNRcLWXiLFcF4F+i1S2QAusCMszlQeJV84UetEkczjUdJ4dWbnpRkeAmXCTzRHyO67XKS6GSCuKVO/sf7cyll0i6e+d0MSWB2CTxojYingZSV6ZQO8K1Z3fJyFYSHiRhDwJ4idC80zTyKagsWV29uNa38iQYr4FwbNqZAgMBAAECgYAxV1k6W1eMMg0OsKeRabQVuwoNG3tJEnQtDdSu0zKg3vdohAyh6MR7EvmiA7g86HH8CsPd/y/9WJe/8j6sBO0Ye9gt7eyQ2NiwWvlTuwNmngcSTapVvVI6NEyJFMfQt9PB1EHLNAXlz8jtJUyA7C48jReQD9p/SzAP0VxG7lwyMQJBAOjE7hAZ/6fyP3DB1fG7jr9gONZcz3TUaqx6BUn4GKZnckW08ht9Xqcqft5Hthu8BbLM9ptQ0U8QZekrJwD6ya0CQQClwstZMPu8jLhsgugVwodcG1mPEOiw9Yjnmt9+WTI07Ll2uFv//hRXBnahBBnZbucUYEbUY3kqUX9b3e9TmEodAkEAybPMbxt4VDoxCy6Mi/pxChkBZ4/pHV3sSiU6bAyWn6vIc+sGWRfca5MBePA/N+1IKtY9Y/02QwL8rH5+P/URyQJAL/hdjORGFdzLimuf6pwvPBKWKncEQCHuisghIZmClBpl2duklELddAnkztg2+tvDd/wcw14+NGb9aoKhvhl2aQJAbvcgoPU+xs0CjeexH+TS2S/jKkTRpvP2CpPK/k71m13xWdE8RtMkYY1measRmlIwOfWze7ll/PGT4dxWf31FNg==";
private String dataRSA = "BlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBla12345678";
@Test
public void encryptDecryptRSA() {
try {
genKeyPair();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
public void encryptDecryptRSA() throws Exception {
int keySize = 1024;
Pair<String, String> publicPrivateKey = genKeyPair(keySize);
String publicKey = publicPrivateKey.first;
String privateKey = publicPrivateKey.second;
String dataRSA = "BlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBl";
System.out.println("publicKeyBase64:" + publicKey);
System.out.println("privateKeyBase64:" + privateKey);
System.out.println(EncryptUtils.encryptRSA2HexString(
dataRSA.getBytes(),
base64Decode(publicKey.getBytes()),
keySize,
"RSA/None/PKCS1Padding"
));
assertArrayEquals(EncryptUtils.decryptRSA(
EncryptUtils.encryptRSA(
dataRSA.getBytes(),
base64Decode(publicKey.getBytes()),
true,
"RSA/ECB/PKCS1Padding"
keySize,
"RSA/None/PKCS1Padding"
),
base64Decode(privateKey.getBytes()),
false,
"RSA/ECB/PKCS1Padding"
keySize,
"RSA/None/PKCS1Padding"
), dataRSA.getBytes());
}
......@@ -443,13 +451,25 @@ public class EncryptUtilsTest extends BaseTest {
}
}
private static void genKeyPair() throws NoSuchAlgorithmException {
private Pair<String, String> genKeyPair(int size) throws NoSuchAlgorithmException {
if (size == 1024) {
return Pair.create(
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYHGvdORdwsK5i+s9rKaMPL1O5eDK2XwNHRUWaxmGB/cxLxeinJrrqdAN+mME7XtGN9bklnOR3MUBQLVnWIn/IU0pnIJY9DpPTVc7x+1zFb8UUq1N0BBo/NpUG5olxuQULuAAHZOg28pnP/Pcb5XVEvpNKL0HaWjN8pu/Dzf8gZwIDAQAB",
"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJgca905F3CwrmL6z2spow8vU7l4MrZfA0dFRZrGYYH9zEvF6Kcmuup0A36YwTte0Y31uSWc5HcxQFAtWdYif8hTSmcglj0Ok9NVzvH7XMVvxRSrU3QEGj82lQbmiXG5BQu4AAdk6Dbymc/89xvldUS+k0ovQdpaM3ym78PN/yBnAgMBAAECgYAFdX+pgNMGiFC53KZ1AhmIAfrPPTEUunQzqpjE5Tm6oJEkZwXiedFbeK5nbLQCnXSH07nBT9AjNvFH71i6BqLvT1l3/ezPq9pmRPriHfWQQ3/J3ASf1O9F9CkYbq/s/qqkXEFcl8PdYQV0xU/kS4jZPP+60Lv3sPkLg2DpkhM+AQJBANTl+/v6sBqqQSS0Anl5nE15Ck3XGBcq0nvATHfFkJYtG9rrXz3ZoRATLxF1iJYwGSAtirhev9W7qFayjci0ztcCQQC25/kkFbeMEWT6/kyV8wcPIog1mKy8RVB9+2l6C8AzbWBPZYtLlB7uaGSJeZBTEGfvRYzpFm5xO0JqwCfDddjxAkBmxtgM3wqg9MwaAeSn6/Nu2x4EUfBJTtzp7P19XJzeQsyNtM73ttYwQnKYhRr5FiMrC5FKTENj1QIBSJV17QNlAkAL5cUAAuWgl9UQuo/yxQ81fdKMYfUCfiPBPiRbSv5imf/Eyl8oOGdWrLW1d5HaxVttZgHHe60NcoRce0la3oSRAkAe8OqLsm9ryXNvBtZxSG+1JUvePVxpRSlJdZIAUKxN6XQE0S9aEe/IkNDBgVeiUEtop76R2NkkGtGTwzbzl0gm"
);
} else if (size == 2048) {
return Pair.create(
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjLLeJZIO7dfQKb6tHE+TlhvD1m3UdTefKvl4uNQboDXy2ztgPcksjLDXxsT+znxMBh4RpXxfVPgnrcSLewGVhTb3uXh9sWo6tvvshNaMKBTebaZePhE7grq+LHH3NILscVssK24rDSvIquZ4nUbDipF/Iscge4LwnypcCuun/3RCn4HYzXW+0YFFZC8Vq4zabIxtzzkvgZlAlvuD6tT76Uuo5kD8b36yYNALI+ZStOj283wlL8PgyyitRGaqCH+MjWYqDb5C0DN31kcoSU7ARTGWgNNAoexAdNujkBvVRFyR2cH9FpjJDu18Oa8v9uSjlRftVWPj0OQXE7vRUsrrawIDAQAB",
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCMst4lkg7t19Apvq0cT5OWG8PWbdR1N58q+Xi41BugNfLbO2A9ySyMsNfGxP7OfEwGHhGlfF9U+CetxIt7AZWFNve5eH2xajq2++yE1owoFN5tpl4+ETuCur4scfc0guxxWywrbisNK8iq5nidRsOKkX8ixyB7gvCfKlwK66f/dEKfgdjNdb7RgUVkLxWrjNpsjG3POS+BmUCW+4Pq1PvpS6jmQPxvfrJg0Asj5lK06PbzfCUvw+DLKK1EZqoIf4yNZioNvkLQM3fWRyhJTsBFMZaA00Ch7EB026OQG9VEXJHZwf0WmMkO7Xw5ry/25KOVF+1VY+PQ5BcTu9FSyutrAgMBAAECggEAHJQ4i2kfnzA3GEOi5h1D3TnGjcfBYA3sRs5ltyVedyx+KAnngqVaZzmEmtto5ohY6OUysGqS8q91X9aMfm/T7zs7FnFjFqZ9Rq3lXRY3YezbQWqJuhHGBMfp2R1NGV1+qYfbcPbvx70dBZnK5id5kKv9JxNLhcsTFUGFcLJtbXXixY2CGiS/dIbFvFHGMbAz3+9l9HXaL4AS7KQXvnauwJW1a5vIAVFYZVBj0qY9Viy2vq6ShH+9pdxOSsWBt08WpxIhjkTr+ZkFck67la2Jn0SBlClB0FIygTqbAmsM3p1nqcR55jdx3hfs31rIfM1Rx5epMm48KYErb2ktowngAQKBgQDL9FEumMMagPy4+EjR1puFHNvADlAi8tIUNt1W5zKKnd+T6gYGn8nqiiy5pvwLLUp8JISmq50tMC3cgAPw+G4kIe5zoBO2EU9X6aPhMd/ScUlVdk0IzEMXa3kMAOjOInWvoevJ4cwWcBPH2aRuDg5wZdh3TpB9LQP4uQ0QHwmE3wKBgQCwmkL6rJDrNo1GNUsjw+WIsXkuS3PYJahbg/uhRdGSsX2BRIPQVCRJP7MkgaUMhZRilt1ROfQy4d2BPxTxvUiGJcKfpsW8xi39PrYWZC5TvEA839q39Uak+ISCsYtZaHk5dvzmE9nF5gv0ivjCr81N2/1KwXO8VmNofzWUqNd+9QKBgQCs39QICRgm2Ppd1qXyp1N/SuzBJ+CpHuUOmUqXpLRkZljiSVT+PGar1J8AZhfxaVxfSZzeoUxCxzm4UxIEKK9DFTfG7gKHKrj0LWfpM5siB0A/nlzBflHIAiLCF+s8/lx+mGMB5dBVnH5HwaTsXCHFB66pwgAa+hMJueDmr0gkRQKBgDKhd1Rwxvd4Y1ZejxVI43SmFOzt2t98JGFgXHLnFmdtFWNLJlNC3EhXx99Of+gwH9OIFxljeRxhXuTgFfwcXT+AceTdplExrBuvr/qJbDK7hNsu/oDBBCjlyu/BQQc4CZEtCOJZjJTNGF5avWjrh/urd1nITosPZV6fIdhl86pFAoGAfOwK0Wte6gO5glAHP9RNktDeyFJCfFH1KUFiAG7XUww6bRpL2fEAqBIcDVgsS565ihxDSbUjgQgg/Ckh2+iBrwf1K9ViO4XUuwWqRS26rn4Is/W5kbPtnC4HS5cQIH1aWi3xUMJcWxV4ZrwiMVdw91leYWC0IbXC/yrc/PBW+sE="
);
}
SecureRandom secureRandom = new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024, secureRandom);
keyPairGenerator.initialize(size, secureRandom);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
......@@ -463,10 +483,6 @@ public class EncryptUtilsTest extends BaseTest {
String publicKeyBase64 = EncodeUtils.base64Encode2String(publicKeyBytes);
String privateKeyBase64 = EncodeUtils.base64Encode2String(privateKeyBytes);
System.out.println("publicKeyBase64.length():" + publicKeyBase64.length());
System.out.println("publicKeyBase64:" + publicKeyBase64);
System.out.println("privateKeyBase64.length():" + privateKeyBase64.length());
System.out.println("privateKeyBase64:" + privateKeyBase64);
return Pair.create(publicKeyBase64, privateKeyBase64);
}
}
\ No newline at end of file
......@@ -402,6 +402,34 @@ public class ThreadUtilsTest extends BaseTest {
countDownLatch.await();
}
@Test
public void testTimeout() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
ThreadUtils.SimpleTask<Boolean> task = new ThreadUtils.SimpleTask<Boolean>() {
@Override
public Boolean doInBackground() throws Throwable {
System.out.println("doInBackground start");
Thread.sleep(2000);
System.out.println("doInBackground end");
return null;
}
@Override
public void onSuccess(Boolean result) {
System.out.println("onSuccess");
}
};
task.setTimeout(1000, new ThreadUtils.Task.OnTimeoutListener() {
@Override
public void onTimeout() {
System.out.println("onTimeout");
}
});
ThreadUtils.executeByCached(task);
latch.await(3, TimeUnit.SECONDS);
}
abstract static class TestScheduledTask<T> extends ThreadUtils.Task<T> {
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
......
# Change Log
## v1.0(2019/07/)
## v1.0(2019/07/20)
发布初版本
\ No newline at end of file
......@@ -351,4 +351,7 @@ public abstract static class BaseApi {
这段代码很好理解,而且加了同步锁操作,防止多线程生成多个 `impl`,然后,根据传进来的 `api` 的 class,我们通过注入的 `map` 中找到具体的 `impl` 的 class,如果缓存中有就取缓存中的,没有的话就通过 `newInstance` 来实例化一个 `impl`,并放入缓存中,最终返回其 `impl`。因为是通过 `newInstance` 来实例化 `impl`,这也解释了为什么 `impl` 中需保留无参构造函数,而且只有在使用时才会初始化,而不是一股脑把所有的 `api` 都初始化。
简易实用,不到 100 行代码实现模块间跳转的 `ApiUtils` 已介绍完毕,接下来你就可以小试牛刀了。
\ No newline at end of file
简易实用,不到 100 行代码实现模块间跳转的 `ApiUtils` 已介绍完毕,接下来你就可以小试牛刀了。
## [Change Log](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/api-gradle-plugin/CHANGELOG.md)
\ No newline at end of file
# Change Log
## v2.1
支持 Tag 一对多,同 Tag 可设置事件优先级
## v2.0
更改为 EventBus 模式
......
......@@ -19,7 +19,7 @@
buildscript {
dependencies {
...
classpath 'com.blankj:bus-gradle-plugin:2.0'
classpath 'com.blankj:bus-gradle-plugin:2.1'
}
}
```
......@@ -36,10 +36,10 @@ apply plugin: "com.blankj.bus"
api "com.blankj:utilcode:latest_version
```
如果你单纯只想引入 `BusUtils` 也是可以的,需要你自己拷贝一份这个类放到你工程里,记得还要拷贝 `ThreadUtils` 哦,然后在 app 下的 `build.gradle` 中 配置 bus 的 SDL 域如下所示:
如果你单纯只想引入 `BusUtils` 也是可以的,需要你自己拷贝一份这个类放到你工程里,记得还要拷贝 `ThreadUtils` 哦,然后在 app 下的 `build.gradle` 中 配置 bus 的 DSL 域如下所示:
```groovy
api {
bus {
busUtilsClass "com.xxx.xxx.BusUtils"
}
......@@ -425,7 +425,7 @@ public void compareUnregister10000Times() {
}
```
同理,如果两个 bus 的 `Tag` 相同了,也会编译不过,提示你项目中存在 `Tag` 相同的 bus。
~~同理,如果两个 bus 的 `Tag` 相同了,也会编译不过,提示你项目中存在 `Tag` 相同的 bus。~~(2.1 版本已支持 Tag 一对多及事件优先级)
所以,`BusUtils``EventBus` 更友好。
......@@ -749,4 +749,7 @@ private void unregisterInner(final Object bus) {
}
```
`unregister``register` 相反,就是从 `mClassName_BusesMap` 的 value 集合中移除,同样需要对 `mClassName_BusesMap` 加锁哦。
\ No newline at end of file
`unregister``register` 相反,就是从 `mClassName_BusesMap` 的 value 集合中移除,同样需要对 `mClassName_BusesMap` 加锁哦。
## [Change Log](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/bus-gradle-plugin/CHANGELOG.md)
\ No newline at end of file
......@@ -62,6 +62,8 @@ public class BusClassVisitor extends ClassVisitor {
tag = (String) value;
} else if ("sticky".equals(name) && (Boolean) value) {
busInfo.sticky = true;
} else if ("priority".equals(name)) {
busInfo.priority = (int) value;
}
}
......@@ -84,7 +86,6 @@ public class BusClassVisitor extends ClassVisitor {
if ("this".equals(name)) {
return;
}
System.out.println("funParamDesc: " + funParamDesc + ", desc: " + desc);
funParamDesc = funParamDesc.substring(desc.length());// 每次去除参数直到为 "",那么之后的就不是参数了
busInfo.paramsInfo.add(new BusInfo.ParamsInfo(Type.getType(desc).getClassName(), name));
if (busInfo.isParamSizeNoMoreThanOne && busInfo.paramsInfo.size() > 1) {
......@@ -101,15 +102,6 @@ public class BusClassVisitor extends ClassVisitor {
if (infoList == null) {
infoList = new ArrayList<>();
mBusMap.put(tag, infoList);
} else if (infoList.size() == 0) {
mBusMap.put(tag, infoList);
} else if (infoList.size() == 1) {
BusInfo info0 = infoList.get(0);
info0.isTagRepeat = true;
busInfo.isTagRepeat = true;
} else {
busInfo.isTagRepeat = true;
}
infoList.add(busInfo);
}
......
......@@ -18,8 +18,8 @@ public class BusInfo {
public List<ParamsInfo> paramsInfo; // 参数列表信息
public boolean sticky; // 是否粘性
public String threadMode; // 线程模式
public int priority; // 优先级
public boolean isParamSizeNoMoreThanOne; // 参数是否不多于 1 个
public boolean isTagRepeat; // 是否存在相同的 tag
public BusInfo(String className, String funName) {
this.className = className;
......@@ -27,8 +27,8 @@ public class BusInfo {
paramsInfo = new ArrayList<>();
sticky = false;
threadMode = "POSTING";
priority = 0;
isParamSizeNoMoreThanOne = true;
isTagRepeat = false;
}
@Override
......@@ -37,8 +37,8 @@ public class BusInfo {
return "{ desc: " + className + "#" + funName +
"(" + paramsInfoString.substring(1, paramsInfoString.length() - 1) + ")" +
(!sticky ? "" : ", sticky: true") +
", threadMode: " + threadMode +
(!isTagRepeat ? "" : ", isTagRepeat: true") +
(threadMode.equals("POSTING") ? "" : ", threadMode: " + threadMode) +
(priority == 0 ? "" : ", priority: " + priority) +
(isParamSizeNoMoreThanOne ? "" : ", paramSize: " + paramsInfo.size()) +
" }";
}
......
......@@ -101,22 +101,32 @@ class BusTransform extends Transform {
if (busScan.busMap.isEmpty()) {
LogUtils.l("no bus.")
} else {
Map<String, String> rightBus = [:]
Map wrongBus = [:]
busScan.busMap.each { String tag, List<BusInfo> infoList ->
if (infoList.size() == 1) {
BusInfo busInfo = infoList.get(0)
if (busInfo.isParamSizeNoMoreThanOne) {
rightBus.put(tag, busInfo.toString())
} else {
wrongBus.put(tag, busInfo.toString())
infoList.sort(new Comparator<BusInfo>() {
@Override
int compare(BusInfo t0, BusInfo t1) {
return t1.priority - t0.priority
}
} else {
List<String> infoString = []
infoList.each { BusInfo info ->
infoString.add(info.toString())
})
}
Map<String, List<String>> rightBus = [:]
Map<String, List<String>> wrongBus = [:]
busScan.busMap.each { String tag, List<BusInfo> infoList ->
List<String> rightInfoString = []
List<String> wrongInfoString = []
infoList.each { BusInfo info ->
if (info.isParamSizeNoMoreThanOne) {
rightInfoString.add(info.toString())
} else {
wrongInfoString.add(info.toString())
}
wrongBus.put(tag, infoString)
}
if (!rightInfoString.isEmpty()) {
rightBus.put(tag, rightInfoString)
}
if (!wrongInfoString.isEmpty()) {
wrongBus.put(tag, wrongInfoString)
}
}
Map busDetails = [:]
......
......@@ -53,23 +53,24 @@ public class BusUtilsClassVisitor extends ClassVisitor {
super.onMethodExit(opcode);
for (Map.Entry<String, List<BusInfo>> busEntry : mBusMap.entrySet()) {
List<BusInfo> infoList = busEntry.getValue();
if (infoList.size() != 1) continue;
BusInfo busInfo = infoList.get(0);
if (!busInfo.isParamSizeNoMoreThanOne) continue;
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(busEntry.getKey());
mv.visitLdcInsn(busInfo.className);
mv.visitLdcInsn(busInfo.funName);
if (busInfo.paramsInfo.size() == 1) {
mv.visitLdcInsn(busInfo.paramsInfo.get(0).className);
mv.visitLdcInsn(busInfo.paramsInfo.get(0).name);
} else {
mv.visitLdcInsn("");
mv.visitLdcInsn("");
for (BusInfo busInfo : infoList) {
if (!busInfo.isParamSizeNoMoreThanOne) continue;
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(busEntry.getKey());
mv.visitLdcInsn(busInfo.className);
mv.visitLdcInsn(busInfo.funName);
if (busInfo.paramsInfo.size() == 1) {
mv.visitLdcInsn(busInfo.paramsInfo.get(0).className);
mv.visitLdcInsn(busInfo.paramsInfo.get(0).name);
} else {
mv.visitLdcInsn("");
mv.visitLdcInsn("");
}
mv.visitInsn(busInfo.sticky ? ICONST_1 : ICONST_0);
mv.visitLdcInsn(busInfo.threadMode);
mv.visitIntInsn(SIPUSH, busInfo.priority);
mv.visitMethodInsn(INVOKESPECIAL, mBusUtilsClass, "registerBus", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;I)V", false);
}
mv.visitInsn(busInfo.sticky ? ICONST_1 : ICONST_0);
mv.visitLdcInsn(busInfo.threadMode);
mv.visitMethodInsn(INVOKESPECIAL, mBusUtilsClass, "registerBus", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V", false);
}
}
};
......
......@@ -9,6 +9,7 @@ import org.objectweb.asm.ClassWriter;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -33,8 +34,8 @@ public class BusTest {
System.out.println("noParam");
}
@BusUtils.Bus(tag = TAG_NO_PARAM)
public void sameTagFun() {
@BusUtils.Bus(tag = TAG_NO_PARAM, priority = 1)
public void sameTagP1Fun() {
System.out.println("noParam");
}
......@@ -89,6 +90,14 @@ public class BusTest {
ClassVisitor cv = new BusClassVisitor(cw, busMap, BusUtils.class.getName());
cr.accept(cv, ClassReader.SKIP_FRAMES);
for (List<BusInfo> value : busMap.values()) {
value.sort(new Comparator<BusInfo>() {
@Override
public int compare(BusInfo t0, BusInfo t1) {
return t1.priority - t0.priority;
}
});
}
System.out.println("busMap = " + busMap);
return busMap;
}
......
......@@ -6,10 +6,14 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
......@@ -25,8 +29,10 @@ public final class BusUtils {
private static final Object NULL = "nULl";
private static final String TAG = "BusUtils";
private final Map<String, List<BusInfo>> mTag_BusInfoListMap = new HashMap<>();
private final Map<String, Set<Object>> mClassName_BusesMap = new ConcurrentHashMap<>();
private final Map<String, BusInfo> mTag_BusInfoMap = new HashMap<>();
private final Map<String, List<String>> mClassName_TagsMap = new HashMap<>();
private final Map<String, Map<String, Object>> mClassName_Tag_Arg4StickyMap = new ConcurrentHashMap<>();
private BusUtils() {
......@@ -42,7 +48,18 @@ public final class BusUtils {
private void registerBus(String tag,
String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode) {
mTag_BusInfoMap.put(tag, new BusInfo(className, funName, paramType, paramName, sticky, threadMode));
registerBus(tag, className, funName, paramType, paramName, sticky, threadMode, 0);
}
private void registerBus(String tag,
String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode, int priority) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
busInfoList = new ArrayList<>();
mTag_BusInfoListMap.put(tag, busInfoList);
}
busInfoList.add(new BusInfo(className, funName, paramType, paramName, sticky, threadMode, priority));
}
public static void register(final Object bus) {
......@@ -79,7 +96,7 @@ public final class BusUtils {
@Override
public String toString() {
return "BusUtils: " + mTag_BusInfoMap;
return "BusUtils: " + mTag_BusInfoListMap;
}
private static BusUtils getInstance() {
......@@ -88,7 +105,8 @@ public final class BusUtils {
private void registerInner(final Object bus) {
if (bus == null) return;
String className = bus.getClass().getName();
Class aClass = bus.getClass();
String className = aClass.getName();
synchronized (mClassName_BusesMap) {
Set<Object> buses = mClassName_BusesMap.get(className);
if (buses == null) {
......@@ -97,6 +115,28 @@ public final class BusUtils {
}
buses.add(bus);
}
List<String> tags = mClassName_TagsMap.get(className);
if (tags == null) {
synchronized (mClassName_TagsMap) {
tags = mClassName_TagsMap.get(className);
if (tags == null) {
tags = new ArrayList<>();
for (Map.Entry<String, List<BusInfo>> entry : mTag_BusInfoListMap.entrySet()) {
for (BusInfo busInfo : entry.getValue()) {
try {
if (Class.forName(busInfo.className).isAssignableFrom(aClass)) {
tags.add(entry.getKey());
busInfo.classNames.add(className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
mClassName_TagsMap.put(className, tags);
}
}
}
processSticky(bus);
}
......@@ -128,19 +168,21 @@ public final class BusUtils {
}
private void postInner(final String tag, final Object arg, final boolean sticky) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
System.out.println("The bus of tag <" + tag + "> is not exists.");
return;
}
if (busInfo.method == null) {
Method method = getMethodByBusInfo(busInfo);
if (method == null) {
return;
for (BusInfo busInfo : busInfoList) {
if (busInfo.method == null) {
Method method = getMethodByBusInfo(busInfo);
if (method == null) {
return;
}
busInfo.method = method;
}
busInfo.method = method;
invokeMethod(tag, arg, busInfo, sticky);
}
invokeMethod(tag, arg, busInfo, sticky);
}
private Method getMethodByBusInfo(BusInfo busInfo) {
......@@ -148,7 +190,7 @@ public final class BusUtils {
if ("".equals(busInfo.paramType)) {
return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName);
} else {
return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, Class.forName(busInfo.paramType));
return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, getClassName(busInfo.paramType));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
......@@ -158,6 +200,29 @@ public final class BusUtils {
return null;
}
private Class getClassName(String paramType) throws ClassNotFoundException {
switch (paramType) {
case "boolean":
return boolean.class;
case "int":
return int.class;
case "long":
return long.class;
case "short":
return short.class;
case "byte":
return byte.class;
case "double":
return double.class;
case "float":
return float.class;
case "char":
return char.class;
default:
return Class.forName(paramType);
}
}
private void invokeMethod(final String tag, final Object arg, final BusInfo busInfo, final boolean sticky) {
Runnable runnable = new Runnable() {
@Override
......@@ -187,8 +252,14 @@ public final class BusUtils {
}
private void realInvokeMethod(final String tag, Object arg, BusInfo busInfo, boolean sticky) {
Set<Object> buses = mClassName_BusesMap.get(busInfo.className);
if (buses == null || buses.size() == 0) {
Set<Object> buses = new HashSet<>();
for (String className : busInfo.classNames) {
Set<Object> subBuses = mClassName_BusesMap.get(className);
if (subBuses != null && !subBuses.isEmpty()) {
buses.addAll(subBuses);
}
}
if (buses.size() == 0) {
if (!sticky) {
System.out.println("The bus of tag <" + tag + "> was not registered before.");
return;
......@@ -214,64 +285,72 @@ public final class BusUtils {
}
private void postStickyInner(final String tag, final Object arg) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
System.out.println("The bus of tag <" + tag + "> is not exists.");
return;
}
if (!busInfo.sticky) { // not sticky bus will post directly.
postInner(tag, arg);
return;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null) {
tagArgMap = new HashMap<>();
mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);
for (BusInfo busInfo : busInfoList) {
if (!busInfo.sticky) { // not sticky bus will post directly.
postInner(tag, arg);
return;
}
tagArgMap.put(tag, arg);
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null) {
tagArgMap = new HashMap<>();
mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);
}
tagArgMap.put(tag, arg);
}
postInner(tag, arg, true);
}
postInner(tag, arg, true);
}
private void removeStickyInner(final String tag) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);
if (busInfoList == null) {
System.out.println("The bus of tag <" + tag + "> is not exists.");
return;
}
if (!busInfo.sticky) {
System.out.println("The bus of tag <" + tag + "> is not sticky.");
return;
}
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null || !tagArgMap.containsKey(tag)) {
System.out.println("The sticky bus of tag <" + tag + "> didn't post.");
for (BusInfo busInfo : busInfoList) {
if (!busInfo.sticky) {
System.out.println("The bus of tag <" + tag + "> is not sticky.");
return;
}
tagArgMap.remove(tag);
synchronized (mClassName_Tag_Arg4StickyMap) {
Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);
if (tagArgMap == null || !tagArgMap.containsKey(tag)) {
System.out.println("The sticky bus of tag <" + tag + "> didn't post.");
return;
}
tagArgMap.remove(tag);
}
}
}
private static final class BusInfo {
String className;
String funName;
String paramType;
String paramName;
boolean sticky;
String threadMode;
Method method;
String className;
String funName;
String paramType;
String paramName;
boolean sticky;
String threadMode;
int priority;
Method method;
List<String> classNames;
BusInfo(String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode) {
boolean sticky, String threadMode, int priority) {
this.className = className;
this.funName = funName;
this.paramType = paramType;
this.paramName = paramName;
this.sticky = sticky;
this.threadMode = threadMode;
this.priority = priority;
this.classNames = new CopyOnWriteArrayList<>();
}
@Override
......@@ -281,6 +360,7 @@ public final class BusUtils {
", sticky: " + sticky +
", threadMode: " + threadMode +
", method: " + method +
", priority: " + priority +
" }";
}
}
......@@ -297,6 +377,8 @@ public final class BusUtils {
boolean sticky() default false;
ThreadMode threadMode() default ThreadMode.POSTING;
int priority() default 0;
}
private static class LazyHolder {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册