Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
Simple-Sms
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zhangchengbo
Simple-Sms
Commits
f54ad0d2
Commit
f54ad0d2
authored
Nov 28, 2025
by
zhangchengbo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:添加[黑名单]工具类与javaBean
parent
a91b8262
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
711 additions
and
0 deletions
+711
-0
RecentsHelperCommon.kt
.../simplemobiletools/commons/helpers/RecentsHelperCommon.kt
+234
-0
RecentCall.kt
...kotlin/com/simplemobiletools/commons/models/RecentCall.kt
+40
-0
SIMAccount.kt
...kotlin/com/simplemobiletools/commons/models/SIMAccount.kt
+7
-0
AESCommon.java
...kotlin/com/simplemobiletools/commons/utils/AESCommon.java
+267
-0
PhoneFromUtilCommon.kt
...om/simplemobiletools/commons/utils/PhoneFromUtilCommon.kt
+79
-0
PhoneUtilsCommon.kt
...n/com/simplemobiletools/commons/utils/PhoneUtilsCommon.kt
+84
-0
No files found.
commons/src/main/kotlin/com/simplemobiletools/commons/helpers/RecentsHelperCommon.kt
0 → 100644
View file @
f54ad0d2
package
com.simplemobiletools.commons.helpers
import
android.annotation.SuppressLint
import
android.content.Context
import
android.net.Uri
import
android.provider.CallLog.Calls
import
com.simplemobiletools.commons.extensions.*
import
com.simplemobiletools.commons.models.RecentCall
import
com.simplemobiletools.commons.models.SIMAccount
import
com.simplemobiletools.commons.models.contacts.Contact
class
RecentsHelperCommon
(
private
val
context
:
Context
)
{
private
val
COMPARABLE_PHONE_NUMBER_LENGTH
=
9
private
val
QUERY_LIMIT
=
200
private
val
contentUri
=
Calls
.
CONTENT_URI
/**
* 获取导出通话记录 .json文件
*/
fun
getRecentCalls
(
groupSubsequentCalls
:
Boolean
,
maxSize
:
Int
=
QUERY_LIMIT
,
callback
:
(
MutableList
<
RecentCall
>)
->
Unit
)
{
val
privateCursor
=
context
.
getMyContactsCursor
(
favoritesOnly
=
false
,
withPhoneNumbersOnly
=
true
)
ensureBackgroundThread
{
if
(!
context
.
hasPermission
(
PERMISSION_READ_CALL_LOG
))
{
callback
(
ArrayList
())
return
@ensureBackgroundThread
}
ContactsHelper
(
context
).
getContacts
(
showOnlyContactsWithNumbers
=
true
)
{
contacts
:
java
.
util
.
ArrayList
<
Contact
>
->
val
privateContacts
=
MyContactsContentProvider
.
getContacts
(
context
,
privateCursor
)
if
(
privateContacts
.
isNotEmpty
())
{
contacts
.
addAll
(
privateContacts
)
}
getRecents
(
contacts
,
groupSubsequentCalls
,
maxSize
,
callback
=
callback
)
}
}
}
@SuppressLint
(
"NewApi"
)
private
inline
fun
getRecents
(
contacts
:
List
<
Contact
>,
groupSubsequentCalls
:
Boolean
,
maxSize
:
Int
,
callback
:
(
MutableList
<
RecentCall
>)
->
Unit
)
{
val
recentCalls
=
mutableListOf
<
RecentCall
>()
var
previousRecentCallFrom
=
""
var
previousStartTS
=
0
val
contactsNumbersMap
=
HashMap
<
String
,
String
>()
val
contactPhotosMap
=
HashMap
<
String
,
String
>()
val
projection
=
arrayOf
(
Calls
.
_ID
,
Calls
.
NUMBER
,
Calls
.
CACHED_NAME
,
Calls
.
CACHED_PHOTO_URI
,
Calls
.
DATE
,
Calls
.
DURATION
,
Calls
.
TYPE
,
Calls
.
PHONE_ACCOUNT_ID
,
Calls
.
COUNTRY_ISO
,
Calls
.
IS_READ
,
// Calls.CACHED_MATCHED_NUMBER
)
val
accountIdToSimIDMap
=
HashMap
<
String
,
Int
>()
context
.
getAvailableSIMCardLabels
().
forEach
{
accountIdToSimIDMap
[
it
.
handle
.
id
]
=
it
.
id
}
val
cursor
=
if
(
isNougatPlus
())
{
// https://issuetracker.google.com/issues/175198972?pli=1#comment6
val
limitedUri
=
contentUri
.
buildUpon
()
.
appendQueryParameter
(
Calls
.
LIMIT_PARAM_KEY
,
QUERY_LIMIT
.
toString
())
.
build
()
val
sortOrder
=
"${Calls.DATE} DESC"
context
.
contentResolver
.
query
(
limitedUri
,
projection
,
null
,
null
,
sortOrder
)
}
else
{
val
sortOrder
=
"${Calls.DATE} DESC LIMIT $QUERY_LIMIT"
context
.
contentResolver
.
query
(
contentUri
,
projection
,
null
,
null
,
sortOrder
)
}
val
contactsWithMultipleNumbers
=
contacts
.
filter
{
it
.
phoneNumbers
.
size
>
1
}
val
numbersToContactIDMap
=
HashMap
<
String
,
Int
>()
contactsWithMultipleNumbers
.
forEach
{
contact
->
contact
.
phoneNumbers
.
forEach
{
phoneNumber
->
numbersToContactIDMap
[
phoneNumber
.
value
]
=
contact
.
contactId
numbersToContactIDMap
[
phoneNumber
.
normalizedNumber
]
=
contact
.
contactId
}
}
cursor
?.
use
{
if
(!
cursor
.
moveToFirst
())
{
return
@use
}
do
{
val
id
=
cursor
.
getIntValue
(
Calls
.
_ID
)
var
isUnknownNumber
=
false
val
number
=
cursor
.
getStringValueOrNull
(
Calls
.
NUMBER
)
if
(
number
==
null
||
number
==
"-1"
)
{
isUnknownNumber
=
true
}
var
name
=
cursor
.
getStringValueOrNull
(
Calls
.
CACHED_NAME
)
if
(
name
.
isNullOrEmpty
()
||
name
==
"-1"
)
{
name
=
number
.
orEmpty
()
}
if
(
name
==
number
&&
!
isUnknownNumber
)
{
if
(
contactsNumbersMap
.
containsKey
(
number
))
{
name
=
contactsNumbersMap
[
number
]
!!
}
else
{
val
normalizedNumber
=
number
.
normalizePhoneNumber
()
if
(
normalizedNumber
!!
.
length
>=
COMPARABLE_PHONE_NUMBER_LENGTH
)
{
name
=
contacts
.
filter
{
it
.
phoneNumbers
.
isNotEmpty
()
}.
firstOrNull
{
contact
->
val
curNumber
=
contact
.
phoneNumbers
.
first
().
normalizedNumber
if
(
curNumber
.
length
>=
COMPARABLE_PHONE_NUMBER_LENGTH
)
{
if
(
curNumber
.
substring
(
curNumber
.
length
-
COMPARABLE_PHONE_NUMBER_LENGTH
)
==
normalizedNumber
.
substring
(
normalizedNumber
.
length
-
COMPARABLE_PHONE_NUMBER_LENGTH
)
)
{
contactsNumbersMap
[
number
]
=
contact
.
getNameToDisplay
()
return
@firstOrNull
true
}
}
false
}
?.
name
?:
number
}
}
}
if
(
name
.
isEmpty
()
||
name
==
"-1"
)
{
name
=
"未知"
}
var
photoUri
=
cursor
.
getStringValue
(
Calls
.
CACHED_PHOTO_URI
)
?:
""
if
(
photoUri
.
isEmpty
()
&&
!
number
.
isNullOrEmpty
())
{
if
(
contactPhotosMap
.
containsKey
(
number
))
{
photoUri
=
contactPhotosMap
[
number
]
!!
}
else
{
val
contact
=
contacts
.
firstOrNull
{
it
.
doesContainPhoneNumber
(
number
)
}
if
(
contact
!=
null
)
{
photoUri
=
contact
.
photoUri
contactPhotosMap
[
number
]
=
contact
.
photoUri
}
}
}
val
startTS
=
(
cursor
.
getLongValue
(
Calls
.
DATE
)
/
1000L
).
toInt
()
if
(
previousStartTS
==
startTS
)
{
continue
}
else
{
previousStartTS
=
startTS
}
val
duration
=
cursor
.
getIntValue
(
Calls
.
DURATION
)
val
type
=
cursor
.
getIntValue
(
Calls
.
TYPE
)
val
accountId
=
cursor
.
getStringValue
(
Calls
.
PHONE_ACCOUNT_ID
)
val
simID
=
accountIdToSimIDMap
[
accountId
]
?:
-
1
val
neighbourIDs
=
mutableListOf
<
Int
>()
var
specificNumber
=
""
var
specificType
=
""
val
sfFlag
=
cursor
.
getStringValue
(
Calls
.
COUNTRY_ISO
)
// val export = cursor.getStringValue(Calls.IS_READ)
// val syncHideNumber = cursor.getStringValue(Calls.CACHED_MATCHED_NUMBER)
val
contactIdWithMultipleNumbers
=
numbersToContactIDMap
[
number
]
if
(
contactIdWithMultipleNumbers
!=
null
)
{
val
specificPhoneNumber
=
contacts
.
firstOrNull
{
it
.
contactId
==
contactIdWithMultipleNumbers
}
?.
phoneNumbers
?.
firstOrNull
{
it
.
value
==
number
}
if
(
specificPhoneNumber
!=
null
)
{
specificNumber
=
specificPhoneNumber
.
value
specificType
=
context
.
getPhoneNumberTypeText
(
specificPhoneNumber
.
type
,
specificPhoneNumber
.
label
)
}
}
val
recentCall
=
RecentCall
(
id
=
id
,
phoneNumber
=
number
.
orEmpty
(),
//正常存储,加密放在结束通话
name
=
name
,
photoUri
=
photoUri
,
startTS
=
startTS
,
duration
=
duration
,
type
=
type
,
neighbourIDs
=
neighbourIDs
,
simID
=
simID
,
specificNumber
=
specificNumber
,
specificType
=
specificType
,
isUnknownNumber
=
isUnknownNumber
,
isShunFeng
=
sfFlag
==
""
,
sfNumber
=
number
.
orEmpty
(),
isExport
=
false
,
// isHidePhoneNumber = syncNumber,
)
// if we have multiple missed calls from the same number, show it just once
if
(!
groupSubsequentCalls
||
"$number$name$simID"
!=
previousRecentCallFrom
)
{
recentCalls
.
add
(
recentCall
)
}
else
{
recentCalls
.
lastOrNull
()
?.
neighbourIDs
?.
add
(
id
)
}
previousRecentCallFrom
=
"$number$name$simID"
}
while
(
cursor
.
moveToNext
()
&&
recentCalls
.
size
<
maxSize
)
}
// val blockedNumbers = context.getBlockedNumbers()
//
// val recentResult = recentCalls
// .filter { !context.isNumberBlocked(it.phoneNumber, blockedNumbers) }.toMutableList()
// callback(recentResult)
callback
(
recentCalls
)
}
@SuppressLint
(
"MissingPermission"
)
fun
Context
.
getAvailableSIMCardLabels
():
List
<
SIMAccount
>
{
val
SIMAccounts
=
mutableListOf
<
SIMAccount
>()
try
{
telecomManager
.
callCapablePhoneAccounts
.
forEachIndexed
{
index
,
account
->
val
phoneAccount
=
telecomManager
.
getPhoneAccount
(
account
)
var
label
=
phoneAccount
.
label
.
toString
()
var
address
=
phoneAccount
.
address
.
toString
()
if
(
address
.
startsWith
(
"tel:"
)
&&
address
.
substringAfter
(
"tel:"
).
isNotEmpty
())
{
address
=
Uri
.
decode
(
address
.
substringAfter
(
"tel:"
))
label
+=
" ($address)"
}
val
SIM
=
SIMAccount
(
index
+
1
,
phoneAccount
.
accountHandle
,
label
,
address
.
substringAfter
(
"tel:"
))
SIMAccounts
.
add
(
SIM
)
}
}
catch
(
ignored
:
Exception
)
{
}
return
SIMAccounts
}
}
commons/src/main/kotlin/com/simplemobiletools/commons/models/RecentCall.kt
0 → 100644
View file @
f54ad0d2
package
com.simplemobiletools.commons.models
import
android.os.Parcelable
import
android.telephony.PhoneNumberUtils
import
androidx.annotation.Keep
import
com.simplemobiletools.commons.extensions.normalizePhoneNumber
import
kotlinx.parcelize.Parcelize
/**
* Used at displaying recent calls.
* For contacts with multiple numbers specify the number and type
*/
@Keep
@kotlinx
.
serialization
.
Serializable
@Parcelize
data class
RecentCall
(
val
id
:
Int
,
val
phoneNumber
:
String
,
val
name
:
String
,
val
photoUri
:
String
,
val
startTS
:
Int
,
val
duration
:
Int
,
val
type
:
Int
,
val
neighbourIDs
:
MutableList
<
Int
>,
val
simID
:
Int
,
val
specificNumber
:
String
,
val
specificType
:
String
,
val
isUnknownNumber
:
Boolean
,
var
isShunFeng
:
Boolean
,
var
sfNumber
:
String
,
var
isExport
:
Boolean
=
false
,
//是否已经导出过该条记录
)
:
Parcelable
{
fun
doesContainPhoneNumber
(
text
:
String
):
Boolean
{
val
normalizedText
=
text
.
normalizePhoneNumber
()
return
PhoneNumberUtils
.
compare
(
phoneNumber
.
normalizePhoneNumber
(),
normalizedText
)
||
phoneNumber
.
contains
(
text
)
||
phoneNumber
.
normalizePhoneNumber
().
contains
(
normalizedText
)
||
phoneNumber
.
contains
(
normalizedText
)
}
}
commons/src/main/kotlin/com/simplemobiletools/commons/models/SIMAccount.kt
0 → 100644
View file @
f54ad0d2
package
com.simplemobiletools.commons.models
import
android.telecom.PhoneAccountHandle
import
androidx.annotation.Keep
@Keep
data class
SIMAccount
(
val
id
:
Int
,
val
handle
:
PhoneAccountHandle
,
val
label
:
String
,
val
phoneNumber
:
String
)
commons/src/main/kotlin/com/simplemobiletools/commons/utils/AESCommon.java
0 → 100644
View file @
f54ad0d2
package
com
.
simplemobiletools
.
commons
.
utils
;
import
android.os.Build
;
import
android.util.Base64
;
import
androidx.annotation.RequiresApi
;
import
com.secspace.log.Log
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.nio.charset.Charset
;
import
java.nio.charset.StandardCharsets
;
import
javax.crypto.Cipher
;
import
javax.crypto.CipherInputStream
;
import
javax.crypto.CipherOutputStream
;
import
javax.crypto.spec.IvParameterSpec
;
import
javax.crypto.spec.SecretKeySpec
;
/**
* AES 对称加密算法,加解密工具类
*/
@RequiresApi
(
api
=
Build
.
VERSION_CODES
.
KITKAT
)
public
class
AESCommon
{
private
static
final
String
TAG
=
AESCommon
.
class
.
getSimpleName
()
+
" --> "
;
/**
* 加密算法
*/
private
static
final
String
KEY_ALGORITHM
=
"AES"
;
/**
* AES 的 密钥长度,32 字节,范围:16 - 32 字节
*/
public
static
final
int
SECRET_KEY_LENGTH
=
32
;
/**
* 字符编码
*/
private
static
final
Charset
CHARSET_UTF8
=
StandardCharsets
.
UTF_8
;
/**
* 秘钥长度不足 16 个字节时,默认填充位数
*/
private
static
final
String
DEFAULT_VALUE
=
"0"
;
/**
* 加解密算法/工作模式/填充方式
*/
private
static
final
String
CIPHER_ALGORITHM
=
"AES/ECB/PKCS5Padding"
;
/**
* 加密密码,长度:16 或 32 个字符(随便定义)
*/
private
static
final
String
secretKey
=
"qweasd123"
;
/**
* AES 加密
*
* @param data 待加密内容
* @return 返回Base64转码后的加密数据
*/
public
static
String
encrypt
(
String
data
)
{
try
{
//创建密码器
Cipher
cipher
=
Cipher
.
getInstance
(
CIPHER_ALGORITHM
);
//初始化为加密密码器
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
getSecretKey
(
secretKey
));
byte
[]
encryptByte
=
cipher
.
doFinal
(
data
.
getBytes
(
CHARSET_UTF8
));
// 将加密以后的数据进行 Base64 编码
return
base64Encode
(
encryptByte
);
}
catch
(
Exception
e
)
{
handleException
(
e
);
}
return
null
;
}
/**
* AES 解密
*
* @param base64Data 加密的密文 Base64 字符串
*/
public
static
String
decrypt
(
String
base64Data
)
{
try
{
byte
[]
data
=
base64Decode
(
base64Data
);
Cipher
cipher
=
Cipher
.
getInstance
(
CIPHER_ALGORITHM
);
//设置为解密模式
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
getSecretKey
(
secretKey
));
//执行解密操作
byte
[]
result
=
cipher
.
doFinal
(
data
);
return
new
String
(
result
,
CHARSET_UTF8
);
}
catch
(
Exception
e
)
{
handleException
(
e
);
}
return
null
;
}
/**
* 使用密码获取 AES 秘钥
*/
public
static
SecretKeySpec
getSecretKey
(
String
secretKey
)
{
secretKey
=
toMakeKey
(
secretKey
,
SECRET_KEY_LENGTH
,
DEFAULT_VALUE
);
return
new
SecretKeySpec
(
secretKey
.
getBytes
(
CHARSET_UTF8
),
KEY_ALGORITHM
);
}
/**
* 如果 AES 的密钥小于 {@code length} 的长度,就对秘钥进行补位,保证秘钥安全。
*
* @param secretKey 密钥 key
* @param length 密钥应有的长度
* @param text 默认补的文本
* @return 密钥
*/
private
static
String
toMakeKey
(
String
secretKey
,
int
length
,
String
text
)
{
// 获取密钥长度
int
strLen
=
secretKey
.
length
();
// 判断长度是否小于应有的长度
if
(
strLen
<
length
)
{
// 补全位数
StringBuilder
builder
=
new
StringBuilder
();
// 将key添加至builder中
builder
.
append
(
secretKey
);
// 遍历添加默认文本
for
(
int
i
=
0
;
i
<
length
-
strLen
;
i
++)
{
builder
.
append
(
text
);
}
// 赋值
secretKey
=
builder
.
toString
();
}
return
secretKey
;
}
/**
* 将 Base64 字符串 解码成 字节数组
*/
public
static
byte
[]
base64Decode
(
String
data
)
{
return
Base64
.
decode
(
data
,
Base64
.
NO_WRAP
);
}
/**
* 将 字节数组 转换成 Base64 编码
*/
public
static
String
base64Encode
(
byte
[]
data
)
{
return
Base64
.
encodeToString
(
data
,
Base64
.
NO_WRAP
);
}
/**
* 处理异常
*/
private
static
void
handleException
(
Exception
e
)
{
e
.
printStackTrace
();
Log
.
e
(
TAG
,
TAG
+
e
);
}
/**
* 对文件进行AES加密
*
* @param sourceFile 待加密文件
* @param dir 加密后的文件存储路径
* @param toFileName 加密后的文件名称
* @param secretKey 密钥
* @return 加密后的文件
*/
public
static
File
encryptFile
(
File
sourceFile
,
String
dir
,
String
toFileName
,
String
secretKey
)
{
try
{
// 创建加密后的文件
File
encryptFile
=
new
File
(
dir
,
toFileName
);
// 根据文件创建输出流
FileOutputStream
outputStream
=
new
FileOutputStream
(
encryptFile
);
// 初始化 Cipher
Cipher
cipher
=
initFileAESCipher
(
secretKey
,
Cipher
.
ENCRYPT_MODE
);
// 以加密流写入文件
CipherInputStream
cipherInputStream
=
new
CipherInputStream
(
new
FileInputStream
(
sourceFile
),
cipher
);
// 创建缓存字节数组
byte
[]
buffer
=
new
byte
[
1024
*
2
];
// 读取
int
len
;
// 读取加密并写入文件
while
((
len
=
cipherInputStream
.
read
(
buffer
))
!=
-
1
)
{
outputStream
.
write
(
buffer
,
0
,
len
);
outputStream
.
flush
();
}
// 关闭加密输入流
cipherInputStream
.
close
();
closeStream
(
outputStream
);
return
encryptFile
;
}
catch
(
Exception
e
)
{
handleException
(
e
);
}
return
null
;
}
/**
* AES解密文件
*
* @param sourceFile 源加密文件
* @param dir 解密后的文件存储路径
* @param toFileName 解密后的文件名称
* @param secretKey 密钥
*/
public
static
File
decryptFile
(
File
sourceFile
,
String
dir
,
String
toFileName
,
String
secretKey
)
{
try
{
// 创建解密文件
File
decryptFile
=
new
File
(
dir
,
toFileName
);
// 初始化Cipher
Cipher
cipher
=
initFileAESCipher
(
secretKey
,
Cipher
.
DECRYPT_MODE
);
// 根据源文件创建输入流
FileInputStream
inputStream
=
new
FileInputStream
(
sourceFile
);
// 获取解密输出流
CipherOutputStream
cipherOutputStream
=
new
CipherOutputStream
(
new
FileOutputStream
(
decryptFile
),
cipher
);
// 创建缓冲字节数组
byte
[]
buffer
=
new
byte
[
1024
*
2
];
int
len
;
// 读取解密并写入
while
((
len
=
inputStream
.
read
(
buffer
))
>=
0
)
{
cipherOutputStream
.
write
(
buffer
,
0
,
len
);
cipherOutputStream
.
flush
();
}
// 关闭流
cipherOutputStream
.
close
();
closeStream
(
inputStream
);
return
decryptFile
;
}
catch
(
IOException
e
)
{
handleException
(
e
);
}
return
null
;
}
/**
* 初始化 AES Cipher
*
* @param secretKey 密钥
* @param cipherMode 加密模式
* @return 密钥
*/
private
static
Cipher
initFileAESCipher
(
String
secretKey
,
int
cipherMode
)
{
try
{
// 创建密钥规格
SecretKeySpec
secretKeySpec
=
getSecretKey
(
secretKey
);
// 获取密钥
Cipher
cipher
=
Cipher
.
getInstance
(
"AES/CBC/PKCS5Padding"
);
// 初始化
cipher
.
init
(
cipherMode
,
secretKeySpec
,
new
IvParameterSpec
(
new
byte
[
cipher
.
getBlockSize
()]));
return
cipher
;
}
catch
(
Exception
e
)
{
handleException
(
e
);
}
return
null
;
}
/**
* 关闭流
*
* @param closeable 实现Closeable接口
*/
private
static
void
closeStream
(
Closeable
closeable
)
{
try
{
if
(
closeable
!=
null
)
closeable
.
close
();
}
catch
(
Exception
e
)
{
handleException
(
e
);
}
}
}
commons/src/main/kotlin/com/simplemobiletools/commons/utils/PhoneFromUtilCommon.kt
0 → 100644
View file @
f54ad0d2
package
com.simplemobiletools.commons.utils
import
java.lang.reflect.Field
object
PhoneFromUtilCommon
{
/**
* 获取调用者的包名
*
* @return
*/
// 反射的方式获取mReferrer
fun
reflectGetReferrerA
():
String
{
try
{
val
activityClass
=
Class
.
forName
(
"android.app.Activity"
);
val
refererField
=
activityClass
.
getDeclaredField
(
"mReferrer"
);
refererField
.
isAccessible
=
true
val
referrer
:
String
=
refererField
.
get
(
this
).
toString
()
return
referrer
}
catch
(
e
:
Exception
)
{
return
"No referrer"
}
}
fun
reflectGetReferrer
():
String
{
try
{
val
activityClass
=
Class
.
forName
(
"android.app.Activity"
)
val
refererField
:
Field
=
activityClass
.
getDeclaredField
(
"mReferrer"
)
refererField
.
isAccessible
=
true
return
refererField
.
get
(
this
).
toString
()
}
catch
(
e
:
ClassNotFoundException
)
{
e
.
printStackTrace
()
return
"No referrer"
}
catch
(
e
:
IllegalAccessException
)
{
e
.
printStackTrace
()
return
"No referrer"
}
catch
(
e
:
NoSuchFieldException
)
{
e
.
printStackTrace
()
return
"No referrer"
}
}
/**
* 是否是纯数字 正则
*/
fun
isNumeric
(
str
:
String
):
Boolean
{
return
str
.
matches
(
"\\d+"
.
toRegex
())
}
/**
* 判断字符串是否为纯中文
*
* @param str 输入的字符串
* @return 是否为纯中文
*/
fun
isPureChinese
(
str
:
String
?):
Boolean
{
if
(
str
.
isNullOrEmpty
())
{
return
false
}
// 定义中文字符的 Unicode 范围
val
regex
=
"[\\u4E00-\\u9FA5|\\u3400-\\u4DBF|\\u20000-\\u2A6DF]+"
return
str
.
matches
(
regex
.
toRegex
())
}
fun
containsChinese
(
str
:
String
):
Boolean
{
for
(
i
in
0
until
str
.
length
)
{
val
c
=
str
[
i
]
if
(
c
>=
'\
u4e00
'
&&
c
<=
'\
u9fff
'
||
c
>=
'\
u3400
'
&&
c
<=
'\
u4DBF
'
)
{
return
true
}
}
return
false
}
}
commons/src/main/kotlin/com/simplemobiletools/commons/utils/PhoneUtilsCommon.kt
0 → 100644
View file @
f54ad0d2
package
com.simplemobiletools.commons.utils
/**
* 手机号码处理工具类
* Created by
*/
object
PhoneUtilsCommon
{
/**
* 电话号掩码规则
*/
fun
phoneNumberFormat
(
number
:
String
,
phoneCallBack
:
(
String
)
->
Unit
/*, errorCallback: (String) -> Unit*/
)
{
if
(
number
.
isBlank
()
||
number
.
length
<=
6
)
return
var
phoneResult
=
""
val
numberResult
=
number
.
replace
(
" "
,
""
)
//原始号码
when
(
numberResult
.
length
)
{
7
->
{
val
phone
=
numberResult
.
substring
(
3
,
numberResult
.
length
)
phoneResult
=
"${"
***
$
phone
"}"
phoneCallBack
.
invoke
(
phoneResult
)
}
8
->
{
val
phone
=
numberResult
.
substring
(
4
,
numberResult
.
length
)
phoneResult
=
"${"
****
$
phone
"}"
phoneCallBack
.
invoke
(
phoneResult
)
}
11
->
{
if
(
numberResult
.
substring
(
0
,
1
)
==
"1"
)
{
phoneResult
=
numberResult
.
replace
(
"(\\d{3})\\d{4}(\\d{4})"
.
toRegex
(),
"$1****$2"
)
phoneCallBack
.
invoke
(
phoneResult
)
}
else
{
val
phone1
=
numberResult
.
substring
(
0
,
3
)
val
phone2
=
numberResult
.
substring
(
7
,
numberResult
.
length
)
phoneResult
=
"${"
$
phone1
****
$
phone2
"}"
phoneCallBack
.
invoke
(
phoneResult
)
}
}
12
->
{
val
phone1
=
numberResult
.
substring
(
0
,
4
)
val
phone2
=
numberResult
.
substring
(
9
,
numberResult
.
length
)
phoneResult
=
"${"
$
phone1
****
$
phone2
"}"
phoneCallBack
.
invoke
(
phoneResult
)
}
14
->
{
if
(
numberResult
.
contains
(
"+86"
))
{
val
phoneNum
=
numberResult
.
substring
(
3
,
numberResult
.
length
)
phoneResult
=
phoneNum
.
replace
(
"(\\d{3})\\d{4}(\\d{4})"
.
toRegex
(),
"$1****$2"
)
phoneCallBack
.
invoke
(
phoneResult
)
}
}
else
->
{
if
(
number
.
isNotBlank
())
{
phoneCallBack
.
invoke
(
number
)
}
}
}
// Log.e("shuju", "---phoneNumberFormat---原号码:${numberResult} 掩码后:$phoneResult //手机号长度:${numberResult.length}")
}
/**
* 手机号码脱敏转换
*/
fun
phoneConvert
(
number
:
String
):
String
{
if
(
number
.
isBlank
()
||
number
.
length
<=
6
)
return
""
var
phoneResult
=
""
val
numberResult
=
number
.
replace
(
" "
,
""
)
//原始号码
if
(
numberResult
.
substring
(
0
,
1
)
==
"1"
)
{
phoneResult
=
numberResult
.
replace
(
"(\\d{3})\\d{4}(\\d{4})"
.
toRegex
(),
"$1****$2"
)
}
else
{
val
phone1
=
numberResult
.
substring
(
0
,
3
)
val
phone2
=
numberResult
.
substring
(
7
,
numberResult
.
length
)
phoneResult
=
"${"
$
phone1
****
$
phone2
"}"
}
return
phoneResult
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment