@@ -77,9 +77,17 @@ public final class TngMoneyPacketHook {
private static volatile long sPacketDedupMs = DEFAULT_PACKET_DEDUP_MS ;
private static volatile long sDetailGapMs = DEFAULT_DETAIL_GAP_MS ;
private static volatile boolean sOpenHistoryIfNoTemplate = true ;
/** 断线/拉历史失败时:自动进一下历史页再返回,重建模板与连接 */
private static volatile boolean sAutoBounceHistoryOnDisconnect = true ;
private static volatile long sLastSettingsPollAt ;
private static volatile long sLastAutoHistoryAt ;
private static volatile boolean sAutoHistoryRunning ;
private static volatile long sLastHistoryBounceAt ;
private static volatile boolean sBounceHistoryPending ;
private static volatile long sLastHookStatusAt ;
private static final long HISTORY_BOUNCE_COOLDOWN_MS = 45_000L ;
private static final long HISTORY_BOUNCE_FINISH_DELAY_MS = 2_200L ;
private static final long HOOK_STATUS_COOLDOWN_MS = 20_000L ;
/** 曾拿到过 session, 之后变空 / RPC 鉴权失败 → 视为断线,恢复后强制重拉 */
private static volatile boolean sHadSession ;
private static volatile boolean sSessionDisconnected ;
@@ -119,7 +127,37 @@ public final class TngMoneyPacketHook {
hookLoginStorage ( lpparam ) ;
hookActivityResumeForAutoHistory ( lpparam ) ;
startSettingsPoller ( ) ;
XposedBridge . log ( TAG + " installed for " + lpparam . packageName ) ;
startAutoHistoryPoller ( ) ;
reportHookAlive ( " install " ) ;
XposedBridge . log ( TAG + " installed for " + lpparam . packageName
+ " mod= " + HookBridge . MODULE_VERSION_NAME
+ " / " + HookBridge . MODULE_VERSION_CODE ) ;
}
/** 按历史冷却定时拉列表 + 心跳,避免无新红包时状态条误报「久无数据」 */
private static void startAutoHistoryPoller ( ) {
Thread t = new Thread ( ( ) - > {
while ( true ) {
try {
reportHookAlive ( " poll " ) ;
scheduleAutoHistory ( " poll " ) ;
long sleepMs = Math . max ( 5_000L , sHistoryCooldownMs ) ;
Thread . sleep ( sleepMs ) ;
} catch ( InterruptedException ie ) {
Thread . currentThread ( ) . interrupt ( ) ;
return ;
} catch ( Throwable ignored ) {
try {
Thread . sleep ( Math . max ( 5_000L , sHistoryCooldownMs ) ) ;
} catch ( InterruptedException ie2 ) {
Thread . currentThread ( ) . interrupt ( ) ;
return ;
}
}
}
} , " TngMmp-auto-hist-poll " ) ;
t . setDaemon ( true ) ;
t . start ( ) ;
}
/** 后台轮询领取台 /api/mmp/settings, 约 10 秒内生效 */
@@ -186,20 +224,24 @@ public final class TngMoneyPacketHook {
long dedupMs = Math . max ( 0 , json . optInt ( " dedupSec " , 3 ) ) * 1000L ;
long gapMs = Math . max ( 0 , json . optInt ( " detailGapMs " , 80 ) ) ;
boolean openHistory = json . optBoolean ( " openHistoryIfNoTemplate " , true ) ;
boolean bounceHistory = json . optBoolean ( " autoBounceHistoryOnDisconnect " , true ) ;
boolean changed = historyMs ! = sHistoryCooldownMs
| | detailMs ! = sDetailCooldownMs
| | dedupMs ! = sPacketDedupMs
| | gapMs ! = sDetailGapMs
| | openHistory ! = sOpenHistoryIfNoTemplate ;
| | openHistory ! = sOpenHistoryIfNoTemplate
| | bounceHistory ! = sAutoBounceHistoryOnDisconnect ;
sHistoryCooldownMs = historyMs ;
sDetailCooldownMs = detailMs ;
sPacketDedupMs = dedupMs ;
sDetailGapMs = gapMs ;
sOpenHistoryIfNoTemplate = openHistory ;
sAutoBounceHistoryOnDisconnect = bounceHistory ;
if ( changed ) {
XposedBridge . log ( TAG + " settings applied hist= " + ( historyMs / 1000 )
+ " s detail= " + ( detailMs / 1000 ) + " s dedup= " + ( dedupMs / 1000 )
+ " s gap= " + gapMs + " ms openHist= " + openHistory
+ " bounceHist= " + bounceHistory
+ " from " + urlStr ) ;
}
return true ;
@@ -400,7 +442,7 @@ public final class TngMoneyPacketHook {
}
MmpSnapshot probe = parseSnapshot ( json ) ;
enrichSnapshotFromJavaResult ( result , probe ) ;
if ( probe ! = null & & ! probe . claims . isEmpty ( ) ) {
if ( probe ! = null & & ( ! probe . claims . isEmpty ( ) | | snapshotHasPacketMeta ( probe ) ) ) {
if ( detailReq ! = null ) {
sTemplateDetailRequest = cloneRequestShallow ( detailReq ) ;
sLoginDonorRequest = detailReq ;
@@ -979,16 +1021,20 @@ public final class TngMoneyPacketHook {
}
private static void rememberPacketData ( String packetId , MmpSnapshot snapshot , Object result ) {
if ( snapshot = = null | | snapshot . claims = = null | | snapshot . claims . isEmpty ( ) ) {
if ( snapshot = = null ) {
return ;
}
boolean hasClaims = snapshot . claims ! = null & & ! snapshot . claims . isEmpty ( ) ;
if ( ! hasClaims & & ! snapshotHasPacketMeta ( snapshot ) ) {
return ;
}
String id = ! TextUtils . isEmpty ( packetId ) ? packetId : snapshot . packetId ;
if ( TextUtils . isEmpty ( id ) | | ! looksLikeActivityId ( id ) ) {
// 指纹 id 也记,避免反复刷
if ( ! TextUtils . isEmpty ( id ) ) {
if ( ! TextUtils . isEmpty ( id ) & & hasClaims ) {
PACKETS_WITH_DATA . add ( id ) ;
}
} else {
} else if ( hasClaims ) {
PACKETS_WITH_DATA . add ( id ) ;
}
if ( result ! = null
@@ -1000,7 +1046,7 @@ public final class TngMoneyPacketHook {
if ( ! snapshot . finished ) {
snapshot . finished = evaluateFinished ( snapshot ) ;
}
if ( snapshot . finished & & ! TextUtils . isEmpty ( id ) ) {
if ( snapshot . finished & & hasClaims & & ! TextUtils . isEmpty ( id ) ) {
if ( COMPLETED_PACKETS . add ( id ) ) {
XposedBridge . log ( TAG + " packet DONE stop-refresh id= " + id
+ " claims= " + snapshot . claims . size ( )
@@ -1068,7 +1114,13 @@ public final class TngMoneyPacketHook {
}
private static void notePossibleDisconnectFromEmpty ( Object result , String activityId ) {
// totalCount:0 + 无领取人 常见于缺登录态
// 新发红包无人领取时 activityPoolInfos 为空、totalCount/claimedCount 也常为 0, 不是断线
if ( looksLikeValidEmptyPacket ( result , activityId ) ) {
XposedBridge . log ( TAG + " empty claims but valid summary id= " + activityId
+ " → keep (0 claimants) " ) ;
return ;
}
// totalCount:0 + 无领取人 且无有效摘要时,才怀疑缺登录态
String totalCnt = stringField ( result , " totalCount " , " getTotalCount " ) ;
if ( " 0 " . equals ( totalCnt ) & & sHadSession ) {
XposedBridge . log ( TAG + " empty claims+totalCount0 id= " + activityId
@@ -1078,6 +1130,64 @@ public final class TngMoneyPacketHook {
}
}
/** 详情有 summary( ACTIVE / 有总额)但领取名单为空:刚发出去、还没人领 */
private static boolean looksLikeValidEmptyPacket ( Object result , String activityId ) {
if ( result = = null ) {
return false ;
}
try {
Object summary = null ;
try {
summary = XposedHelpers . callMethod ( result , " getSummaryInfo " ) ;
} catch ( Throwable ignored ) {
}
if ( summary = = null ) {
try {
summary = XposedHelpers . getObjectField ( result , " summaryInfo " ) ;
} catch ( Throwable ignored ) {
}
}
if ( summary = = null ) {
return false ;
}
String id = firstNonEmpty (
stringField ( summary , " activityId " , " getActivityId " ) ,
activityId ) ;
if ( ! looksLikeActivityId ( id ) ) {
return false ;
}
String status = stringField ( summary , " activityStatus " , " getActivityStatus " ) ;
if ( ! TextUtils . isEmpty ( status ) ) {
String up = status . toUpperCase ( Locale . US ) ;
if ( up . contains ( " ACTIVE " ) | | up . contains ( " OPEN " ) | | up . contains ( " PROGRESS " )
| | up . contains ( " FINISH " ) | | up . contains ( " COMPLETE " )
| | up . contains ( " EXPIRE " ) ) {
return true ;
}
}
String total = null ;
try {
Object money = XposedHelpers . callMethod ( summary , " getTotalAmount " ) ;
if ( money ! = null ) {
total = normalizeMoneyText ( String . valueOf (
XposedHelpers . callMethod ( money , " getAmount " ) ) ) ;
}
} catch ( Throwable ignored ) {
}
return ! TextUtils . isEmpty ( total ) ;
} catch ( Throwable ignored ) {
return false ;
}
}
private static boolean snapshotHasPacketMeta ( MmpSnapshot snapshot ) {
return snapshot ! = null
& & looksLikeActivityId ( snapshot . packetId )
& & ( ! TextUtils . isEmpty ( snapshot . totalAmount )
| | ! TextUtils . isEmpty ( snapshot . activityStatus )
| | ! TextUtils . isEmpty ( snapshot . senderName ) ) ;
}
private static boolean fetchDetailByActivityId ( String activityId , String senderUserId ) {
ClassLoader cl = sAppClassLoader ;
ensureRpcTaskAndLogin ( cl ) ;
@@ -1131,13 +1241,31 @@ public final class TngMoneyPacketHook {
}
MmpSnapshot probe = parseSnapshot ( json ) ;
enrichSnapshotFromJavaResult ( result , probe ) ;
if ( probe = = null | | probe . claims . isEmpty ( ) ) {
if ( probe = = null ) {
XposedBridge . log ( TAG + " auto-detail parse null id= " + activityId ) ;
return false ;
}
if ( TextUtils . isEmpty ( probe . packetId ) ) {
probe . packetId = activityId ;
}
if ( probe . claims . isEmpty ( ) ) {
String snippet = json . length ( ) > 240 ? json . substring ( 0 , 240 ) + " ... " : json ;
XposedBridge . log ( TAG + " auto-detail empty claims id= " + activityId
+ " body= " + snippet
+ " userId= " + stringField ( req , " userId " , " getUserId " ) ) ;
notePossibleDisconnectFromEmpty ( result , activityId ) ;
return false ;
if ( ! snapshotHasPacketMeta ( probe ) & & ! looksLikeValidEmptyPacket ( result , activityId ) ) {
notePossibleDisconnectFromEmpty ( result , activityId ) ;
return false ;
}
// 刚发出、还没人领:照样入库,方便列表看到「领取中 0人」
rememberPacketData ( probe . packetId , probe , result ) ;
forwardSnapshot ( probe , " auto: " + activityId , " rpc " ) ;
XposedBridge . log ( TAG + " auto-detail ok(empty) activityId= " + activityId
+ " status= " + probe . activityStatus
+ " total= " + probe . totalAmount
+ " issued= " + probe . issueTime ) ;
noteSessionAlive ( ) ;
return true ;
}
rememberPacketData ( probe . packetId ! = null ? probe . packetId : activityId , probe , result ) ;
forwardSnapshot ( probe , " auto: " + activityId , " rpc " ) ;
@@ -1329,6 +1457,12 @@ public final class TngMoneyPacketHook {
if ( ! PACKAGE . equals ( pkg ) ) {
return ;
}
String actName = act . getClass ( ) . getName ( ) ;
if ( actName . contains ( " MoneyPacketHistoryActivity " )
& & sBounceHistoryPending ) {
scheduleHistoryBounceFinish ( act ) ;
}
reportHookAlive ( " activity-resume " ) ;
if ( sSessionDisconnected ) {
tryRefreshLogin ( sAppClassLoader ) ;
scheduleAutoHistoryForced ( " reconnect-resume " ) ;
@@ -1373,6 +1507,7 @@ public final class TngMoneyPacketHook {
if ( ok ) {
sLastAutoHistoryAt = System . currentTimeMillis ( ) ;
noteSessionAlive ( ) ;
reportHookAlive ( " auto-history-ok " ) ;
}
} catch ( InterruptedException ie ) {
Thread . currentThread ( ) . interrupt ( ) ;
@@ -1389,6 +1524,7 @@ public final class TngMoneyPacketHook {
Object task = sMoneyPacketRpcTask ;
if ( cl = = null | | task = = null ) {
XposedBridge . log ( TAG + " auto-history skip: no rpc task ( " + reason + " ) " ) ;
maybeBounceHistoryPage ( " no-rpc-task " ) ;
return false ;
}
if ( TextUtils . isEmpty ( sCachedSessionId ) ) {
@@ -1402,6 +1538,7 @@ public final class TngMoneyPacketHook {
if ( sHadSession ) {
sSessionDisconnected = true ;
}
maybeBounceHistoryPage ( " no-session " ) ;
return false ;
}
try {
@@ -1422,24 +1559,33 @@ public final class TngMoneyPacketHook {
} finally {
AUTO_HISTORY_SELF . remove ( ) ;
}
if ( result ! = null & & historyHasJobs ( result ) ) {
XposedBridge . log ( TAG + " auto-history ok(template) → schedule details " ) ;
scheduleAutoDetailsFromHistory ( result ) ;
if ( result ! = null ) {
if ( historyHasJobs ( result ) ) {
XposedBridge . log ( TAG + " auto-history ok(template) → schedule details " ) ;
scheduleAutoDetailsFromHistory ( result ) ;
} else {
XposedBridge . log ( TAG + " auto-history ok(template) but empty list " ) ;
}
return true ;
}
// 有模板但 RPC 失败:不要误当成「无模板」反复乱跳;按断线处理并短进历史页
XposedBridge . log ( TAG + " auto-history template rpc failed ( " + reason + " ) " ) ;
maybeBounceHistoryPage ( " template-rpc-fail " ) ;
return false ;
}
// 2) 无模板时:自造请求易「非法参数」,直接拉起官方历史页让 App 发正确 RPC
if ( ! sOpenHistoryIfNoTemplate ) {
if ( ! sOpenHistoryIfNoTemplate & & ! sAutoBounceHistoryOnDisconnect ) {
XposedBridge . log ( TAG + " auto-history no template, openHistory disabled ( "
+ reason + " ) " ) ;
return false ;
}
XposedBridge . log ( TAG + " auto-history no template → open HistoryActivity ( " + reason + " ) " ) ;
openMoneyPacketHistoryActivity ( ) ;
openMoneyPacketHistoryActivity ( true ) ;
return false ;
} catch ( Throwable t ) {
XposedBridge . log ( TAG + " auto-history fail: " + t . getMessage ( ) ) ;
markMaybeDisconnected ( t , " auto-history " ) ;
maybeBounceHistoryPage ( " history-exception " ) ;
return false ;
}
}
@@ -1448,11 +1594,32 @@ public final class TngMoneyPacketHook {
return ! extractHistoryJobs ( historyResult ) . isEmpty ( ) ;
}
/** 拉起官方历史页; App 自己的 moneyPacketHistoryList 成功后 Hook 会缓存模板并级联拉详情 */
private static void openMoneyPacketHistoryActivity ( ) {
/** 断线/失败时短进历史页再建连(有冷却,避免刷屏) */
private static void maybeBounceHistoryPage ( String reason ) {
if ( ! sAutoBounceHistoryOnDisconnect ) {
XposedBridge . log ( TAG + " bounce history disabled ( " + reason + " ) " ) ;
return ;
}
openMoneyPacketHistoryActivity ( true ) ;
XposedBridge . log ( TAG + " bounce history for " + reason ) ;
}
/** 拉起官方历史页; bounceBack=true 时约 2 秒后自动 finish 返回 */
private static void openMoneyPacketHistoryActivity ( boolean bounceBack ) {
try {
long now = System . currentTimeMillis ( ) ;
if ( bounceBack ) {
if ( now - sLastHistoryBounceAt < HISTORY_BOUNCE_COOLDOWN_MS ) {
XposedBridge . log ( TAG + " bounce history cooldown skip ageMs= "
+ ( now - sLastHistoryBounceAt ) ) ;
return ;
}
sLastHistoryBounceAt = now ;
sBounceHistoryPending = true ;
}
Context ctx = getContext ( ) ;
if ( ctx = = null ) {
sBounceHistoryPending = false ;
return ;
}
android . content . Intent intent = new android . content . Intent ( ) ;
@@ -1461,17 +1628,43 @@ public final class TngMoneyPacketHook {
intent . addFlags ( android . content . Intent . FLAG_ACTIVITY_NEW_TASK
| android . content . Intent . FLAG_ACTIVITY_NO_ANIMATION ) ;
ctx . startActivity ( intent ) ;
XposedBridge . log ( TAG + " started MoneyPacketHistoryActivity" ) ;
XposedBridge . log ( TAG + " started MoneyPacketHistoryActivity bounce= " + bounceBack ) ;
} catch ( Throwable t ) {
sBounceHistoryPending = false ;
XposedBridge . log ( TAG + " start HistoryActivity fail: " + t . getMessage ( ) ) ;
}
}
private static void scheduleHistoryBounceFinish ( final Object activity ) {
if ( ! ( activity instanceof android . app . Activity ) ) {
return ;
}
final android . app . Activity act = ( android . app . Activity ) activity ;
MAIN_HANDLER . postDelayed ( ( ) - > {
try {
if ( ! sBounceHistoryPending ) {
return ;
}
if ( act . isFinishing ( ) ) {
sBounceHistoryPending = false ;
return ;
}
act . finish ( ) ;
XposedBridge . log ( TAG + " bounce history finished → back " ) ;
} catch ( Throwable t ) {
XposedBridge . log ( TAG + " bounce history finish fail: " + t . getMessage ( ) ) ;
} finally {
sBounceHistoryPending = false ;
}
} , HISTORY_BOUNCE_FINISH_DELAY_MS ) ;
}
private static Object callHistoryListRpc ( Object task , Object req ) {
try {
return XposedHelpers . callMethod ( task , " moneyPacketHistoryList " , req ) ;
} catch ( Throwable t ) {
XposedBridge . log ( TAG + " history rpc fail: " + t . getMessage ( ) ) ;
markMaybeDisconnected ( t , " history-rpc " ) ;
return null ;
}
}
@@ -1688,7 +1881,10 @@ public final class TngMoneyPacketHook {
}
private static void forwardSnapshot ( MmpSnapshot snapshot , String sourceHint , String channel ) {
if ( snapshot = = null | | snapshot . claims . isEmpty ( ) ) {
if ( snapshot = = null ) {
return ;
}
if ( snapshot . claims . isEmpty ( ) & & ! snapshotHasPacketMeta ( snapshot ) ) {
return ;
}
dedupeClaims ( snapshot ) ;
@@ -1739,6 +1935,11 @@ public final class TngMoneyPacketHook {
continue ;
}
String name = stringField ( info , " receiverName " , " getReceiverName " ) ;
String receiverId = firstNonEmpty (
stringField ( info , " receiverId " , " getReceiverId " ) ,
stringField ( info , " userId " , " getUserId " ) ,
stringField ( info , " receiverUserId " , " getReceiverUserId " ) ) ;
String poolId = stringField ( info , " activityPoolId " , " getActivityPoolId " ) ;
String claimTime = firstNonEmpty (
stringField ( info , " gmtModified " , " getGmtModified " ) ,
stringField ( info , " gmtCreate " , " getGmtCreate " ) ,
@@ -1755,21 +1956,36 @@ public final class TngMoneyPacketHook {
if ( TextUtils . isEmpty ( amount ) ) {
amount = normalizeMoneyText ( stringField ( info , " amount " , " getAmount " ) ) ;
}
if ( TextUtils . isEmpty ( name ) ) {
if ( TextUtils . isEmpty ( name ) & & TextUtils . isEmpty ( receiverId ) ) {
continue ;
}
if ( TextUtils . isEmpty ( name ) ) {
name = " 用户 " ;
}
boolean matched = false ;
for ( ClaimLine line : snapshot . claims ) {
if ( name . equals ( line . nickname ) ) {
boolean sameId = ! TextUtils . isEmpty ( receiverId )
& & receiverId . equals ( line . receiverId ) ;
boolean sameName = TextUtils . isEmpty ( receiverId )
& & name . equals ( line . nickname ) ;
if ( sameId | | sameName ) {
if ( TextUtils . isEmpty ( line . claimTime ) & & ! TextUtils . isEmpty ( claimTime ) ) {
line . claimTime = claimTime ;
}
if ( TextUtils . isEmpty ( line . receiverId ) & & ! TextUtils . isEmpty ( receiverId ) ) {
line . receiverId = receiverId ;
}
if ( TextUtils . isEmpty ( line . poolId ) & & ! TextUtils . isEmpty ( poolId ) ) {
line . poolId = poolId ;
}
matched = true ;
}
}
if ( ! matched & & ! TextUtils . isEmpty ( amount ) ) {
ClaimLine line = new ClaimLine ( ) ;
line . nickname = name ;
line . receiverId = receiverId ;
line . poolId = poolId ;
line . amount = amount ;
line . claimTime = claimTime ;
snapshot . claims . add ( line ) ;
@@ -1860,30 +2076,43 @@ public final class TngMoneyPacketHook {
}
java . util . LinkedHashMap < String , ClaimLine > map = new java . util . LinkedHashMap < > ( ) ;
for ( ClaimLine line : snapshot . claims ) {
if ( line = = null | | TextUtils . isEmpty ( line . nickname ) ) {
if ( line = = null ) {
continue ;
}
ClaimLine old = map . get ( line . nickname ) ;
if ( TextUtils . isEmpty ( line . nickname ) & & TextUtils . isEmpty ( line . receiverId ) ) {
continue ;
}
String key = ! TextUtils . isEmpty ( line . receiverId )
? ( " id: " + line . receiverId )
: ( " n: " + line . nickname ) ;
ClaimLine old = map . get ( key ) ;
if ( old = = null ) {
map . put ( line . nickname , line ) ;
map . put ( key , line ) ;
continue ;
}
// 同昵称保留金额更大的;时间有则补上
try {
double a = Double . parseDouble ( old . amount ) ;
double b = Double . parseDouble ( line . amount ) ;
if ( b > a ) {
if ( TextUtils . isEmpty ( line . claimTime ) ) {
line . claimTime = old . claimTime ;
}
map . put ( line . nickname , line ) ;
} else if ( TextUtils . isEmpty ( old . claimTime ) & & ! TextUtils . isEmpty ( line . claimTime ) ) {
old . claimTime = line . claimTime ;
double oldAmt = parseMoneyDouble ( old . amount ) ! = null ? parseMoneyDouble ( old . amount ) : - 1 ;
double newAmt = parseMoneyDouble ( line . amount ) ! = null ? parseMoneyDouble ( line . amount ) : - 1 ;
if ( newAmt > = oldAmt ) {
if ( TextUtils . isEmpty ( line . claimTime ) & & ! TextUtils . isEmpty ( old . claimTime ) ) {
line . claimTime = old . claimTime ;
}
} catch ( Throwable ignored ) {
if ( TextUtils . isEmpty ( line . receiverId ) ) {
line . receiverId = old . receiverId ;
}
if ( TextUtils . isEmpty ( line . poolId ) ) {
line . poolId = old . poolId ;
}
map . put ( key , line ) ;
} else {
if ( TextUtils . isEmpty ( old . claimTime ) & & ! TextUtils . isEmpty ( line . claimTime ) ) {
old . claimTime = line . claimTime ;
}
if ( TextUtils . isEmpty ( old . receiverId ) & & ! TextUtils . isEmpty ( line . receiverId ) ) {
old . receiverId = line . receiverId ;
}
if ( TextUtils . isEmpty ( old . poolId ) & & ! TextUtils . isEmpty ( line . poolId ) ) {
old . poolId = line . poolId ;
}
}
}
snapshot . claims . clear ( ) ;
@@ -2094,6 +2323,18 @@ public final class TngMoneyPacketHook {
ClaimLine line = new ClaimLine ( ) ;
line . nickname = name . trim ( ) ;
line . amount = amount ;
line . receiverId = firstNonEmpty (
readFieldText ( obj , " receiverId " ) ,
readFieldText ( obj , " receiverUserId " ) ,
readFieldText ( obj , " userId " ) ,
readFieldText ( obj , " participantId " ) ) ;
// userName 字段误当 id 时:纯数字/长串才留
if ( ! TextUtils . isEmpty ( line . receiverId ) & & line . receiverId . equals ( name ) ) {
line . receiverId = null ;
}
line . poolId = firstNonEmpty (
readFieldText ( obj , " activityPoolId " ) ,
readFieldText ( obj , " poolId " ) ) ;
line . claimTime = firstNonEmpty (
readFieldText ( obj , " claimTime " ) ,
readFieldText ( obj , " claimedTime " ) ,
@@ -2369,6 +2610,21 @@ public final class TngMoneyPacketHook {
return null ;
}
private static void reportHookAlive ( String reason ) {
long now = System . currentTimeMillis ( ) ;
if ( ! " install " . equals ( reason ) & & now - sLastHookStatusAt < HOOK_STATUS_COOLDOWN_MS ) {
return ;
}
Context ctx = getContext ( ) ;
if ( ctx = = null ) {
return ;
}
sLastHookStatusAt = now ;
HookForwarder . reportStatus ( ctx , PACKAGE , HookBridge . SOURCE_XPOSED_TNG_MMP ) ;
XposedBridge . log ( TAG + " hook status reported ( " + reason + " ) mod= "
+ HookBridge . MODULE_VERSION_NAME ) ;
}
private static Context getContext ( ) {
try {
Class < ? > activityThread = XposedHelpers . findClass ( " android.app.ActivityThread " , null ) ;
@@ -2461,6 +2717,8 @@ public final class TngMoneyPacketHook {
sb . append ( " | done=1 " ) ;
}
sb . append ( " | via= " ) . append ( sanitizeMeta ( channel ) ) ;
sb . append ( " | mod= " ) . append ( HookBridge . MODULE_VERSION_NAME )
. append ( '/' ) . append ( HookBridge . MODULE_VERSION_CODE ) ;
if ( ! TextUtils . isEmpty ( sourceHint ) ) {
String src = sourceHint . length ( ) > 120
? sourceHint . substring ( 0 , 120 ) + " ... " : sourceHint ;
@@ -2472,6 +2730,12 @@ public final class TngMoneyPacketHook {
if ( ! TextUtils . isEmpty ( line . claimTime ) ) {
sb . append ( " ( " ) . append ( line . claimTime ) . append ( ')' ) ;
}
if ( ! TextUtils . isEmpty ( line . receiverId ) ) {
sb . append ( " #rid= " ) . append ( sanitizeMeta ( line . receiverId ) ) ;
}
if ( ! TextUtils . isEmpty ( line . poolId ) ) {
sb . append ( " #pool= " ) . append ( sanitizeMeta ( line . poolId ) ) ;
}
sb . append ( '\n' ) ;
}
return sb . toString ( ) . trim ( ) ;
@@ -2488,6 +2752,8 @@ public final class TngMoneyPacketHook {
private static final class ClaimLine {
String nickname ;
String receiverId ;
String poolId ;
String amount ;
String claimTime ;
}