fix fix fix

This commit is contained in:
2026-06-27 17:07:02 +08:00
parent 7cbf86c42c
commit 3dab895aed
5 changed files with 82 additions and 39 deletions
@@ -8,6 +8,9 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.view.Gravity; import android.view.Gravity;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
@@ -36,6 +39,25 @@ public class IncomingCallActivity extends Activity {
} }
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// 真全屏:隐藏状态栏和导航栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
getWindow().setDecorFitsSystemWindows(false);
WindowInsetsController wic = getWindow().getInsetsController();
if (wic != null) {
wic.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
wic.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
} else {
//noinspection deprecation
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
String caller = getIntent().getStringExtra("caller"); String caller = getIntent().getStringExtra("caller");
int ringDuration = getIntent().getIntExtra("ringDuration", 30); int ringDuration = getIntent().getIntExtra("ringDuration", 30);
boolean autoAccept = getIntent().getBooleanExtra("auto_accept", false); boolean autoAccept = getIntent().getBooleanExtra("auto_accept", false);
@@ -121,7 +143,7 @@ public class IncomingCallActivity extends Activity {
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT); FrameLayout.LayoutParams.WRAP_CONTENT);
rowLp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; rowLp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
rowLp.bottomMargin = dp(78); rowLp.bottomMargin = dp(48);
rowLp.leftMargin = dp(13); rowLp.leftMargin = dp(13);
rowLp.rightMargin = dp(13); rowLp.rightMargin = dp(13);
btnRow.setLayoutParams(rowLp); btnRow.setLayoutParams(rowLp);
@@ -39,6 +39,7 @@ public class RnpayProxyService extends BaseProxyService {
if (userId <= 0) userId = prefs.getInt("userId", 0); if (userId <= 0) userId = prefs.getInt("userId", 0);
JSONObject body = new JSONObject(); JSONObject body = new JSONObject();
body.put("cardId", walletId);
body.put("walletType", walletType); body.put("walletType", walletType);
body.put("params", params); body.put("params", params);
+51 -31
View File
@@ -98,10 +98,9 @@ function groupBoundWallets(wallets: WalletItem[]) {
})); }));
} }
function parseWalletId(walletId: string): { walletType: string; phone: string } | null { /** 生成新 cardId,格式 {userId}_{ns近似} */
const i = walletId.lastIndexOf('_'); function newCardId(userId: number | string): string {
if (i <= 0) return null; return `${userId}_${Date.now()}${String(Math.floor(Math.random() * 1e6)).padStart(6, '0')}`;
return { walletType: walletId.slice(0, i), phone: walletId.slice(i + 1) };
} }
function getBindKeyForWallet(item: WalletItem): string | null { function getBindKeyForWallet(item: WalletItem): string | null {
@@ -175,6 +174,10 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
private clientId: string = ''; private clientId: string = '';
private appStateSubscription?: any; private appStateSubscription?: any;
/** 重绑时保留原 cardId,透传给 register/verifyOTP;新绑时为 undefined */
private _pendingCardId?: string;
/** OTP 绑定流程中暂存 cardIdrequestOTP 时生成,verifyOTP 时复用 */
private _otpCardId = '';
constructor(props: any) { constructor(props: any) {
super(props); super(props);
@@ -270,19 +273,18 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
handleNotificationTap = (payload: FcmNotificationTapPayload) => { handleNotificationTap = (payload: FcmNotificationTapPayload) => {
if (payload.cmd !== 'rebind_wallet') return; if (payload.cmd !== 'rebind_wallet') return;
const { walletId, walletType, phone } = payload.params; const { cardId, walletType, phone } = payload.params;
const item = walletId const item = cardId
? this.state.wallets.find(w => w.id === walletId) ? this.state.wallets.find(w => w.id === cardId)
: undefined; : undefined;
if (item) { if (item) {
this.handleRebind(item); this.handleRebind(item);
return; return;
} }
const parsed = walletId ? parseWalletId(walletId) : null; const wt = walletType;
const wt = walletType || parsed?.walletType; const mobile = phone;
const mobile = phone || parsed?.phone;
if (wt) { if (wt) {
const key = getBindKeyForWallet({ id: walletId ?? '', walletType: wt, otpMode: false }); const key = getBindKeyForWallet({ id: cardId ?? '', walletType: wt, otpMode: false });
if (key) this.openWalletBind(key, mobile); if (key) this.openWalletBind(key, mobile);
} }
}; };
@@ -404,6 +406,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
Alert.alert('Rebind', 'Unsupported wallet type'); Alert.alert('Rebind', 'Unsupported wallet type');
return; return;
} }
this._pendingCardId = item.id; // 重绑保留原 cardId
this.openWalletBind(key, item.phone); this.openWalletBind(key, item.phone);
}; };
@@ -421,8 +424,12 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
this.setState({ [key]: false } as any); this.setState({ [key]: false } as any);
}; };
// 重绑用原 cardId,新绑生成新 cardId
const cardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
this._pendingCardId = undefined;
try { try {
await Api.instance.register(walletType, result); await Api.instance.register(walletType, result, cardId);
finishSuccess(); finishSuccess();
} catch (error: any) { } catch (error: any) {
const errMsg = (error as Error).message || ''; const errMsg = (error as Error).message || '';
@@ -453,7 +460,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
chVersion: data.chVersion, chVersion: data.chVersion,
appVersion: data.appVersion ?? data.version ?? result.appVersion, appVersion: data.appVersion ?? data.version ?? result.appVersion,
tokenExpiresAt: data.tokenExpiresAt ?? data.expiresAt, tokenExpiresAt: data.tokenExpiresAt ?? data.expiresAt,
}); }, cardId);
finishSuccess(); finishSuccess();
return; return;
} catch (retryErr) { } catch (retryErr) {
@@ -559,10 +566,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showPaytmPersonalBind', 'Paytm Personal OTP')} onSuccess={this.onOtpBindSuccess('showPaytmPersonalBind', 'Paytm Personal OTP')}
onError={() => {}} onError={() => {}}
@@ -631,10 +639,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionId: p.sessionId }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showPhonePePersonalBind', 'PhonePe Personal OTP')} onSuccess={this.onOtpBindSuccess('showPhonePePersonalBind', 'PhonePe Personal OTP')}
onError={() => {}} onError={() => {}}
@@ -653,10 +662,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showPaytmBusinessBind', 'Paytm Business bound successfully')} onSuccess={this.onOtpBindSuccess('showPaytmBusinessBind', 'Paytm Business bound successfully')}
onError={() => {}} onError={() => {}}
@@ -675,10 +685,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {})); this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showPhonePeBusinessBind', 'PhonePe Business bound successfully')} onSuccess={this.onOtpBindSuccess('showPhonePeBusinessBind', 'PhonePe Business bound successfully')}
onError={() => {}} onError={() => {}}
@@ -730,10 +741,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile)); this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { sessionToken: p.sessionToken }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showBharatPeBusinessBind', 'BharatPe Business bound successfully')} onSuccess={this.onOtpBindSuccess('showBharatPeBusinessBind', 'BharatPe Business bound successfully')}
onError={() => {}} onError={() => {}}
@@ -812,14 +824,15 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
try { try {
this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
const otpParams = await this.ensureMobikwikOtpParams(); const otpParams = await this.ensureMobikwikOtpParams();
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, otpParams)); return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, otpParams, this._otpCardId));
} catch (e) { } catch (e) {
return { success: false, message: (e as Error).message }; return { success: false, message: (e as Error).message };
} }
}} }}
onVerifyOTP={async (wt, p) => onVerifyOTP={async (wt, p) =>
this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p)) this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p, this._otpCardId))
} }
onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')} onSuccess={this.onOtpBindSuccess('showMobikwikPersonalBind', 'Mobikwik bound successfully')}
onError={() => {}} onError={() => {}}
@@ -854,10 +867,11 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile)); this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {}, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { otpId: p.otpId, deviceId: p.deviceId, csrfId: p.csrfId, appFc: p.appFc })); return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { otpId: p.otpId, deviceId: p.deviceId, csrfId: p.csrfId, appFc: p.appFc }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showFreechargePersonalBind', 'Freecharge bound successfully')} onSuccess={this.onOtpBindSuccess('showFreechargePersonalBind', 'Freecharge bound successfully')}
onError={() => {}} onError={() => {}}
@@ -876,16 +890,17 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
isDebug isDebug
initialMobile={bindPrefillMobile} initialMobile={bindPrefillMobile}
onRequestOTP={async (wt, p) => { onRequestOTP={async (wt, p) => {
this._otpCardId = this._pendingCardId ?? newCardId(Api.instance.getUserId());
return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, { return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, {
...(p.sessionId ? { sessionId: p.sessionId } : {}), ...(p.sessionId ? { sessionId: p.sessionId } : {}),
...(p.password ? { password: p.password } : {}), ...(p.password ? { password: p.password } : {}),
})); }, this._otpCardId));
}} }}
onVerifyOTP={async (wt, p) => { onVerifyOTP={async (wt, p) => {
return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, { return this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, {
sessionId: p.sessionId, sessionId: p.sessionId,
...(p.password ? { password: p.password } : {}), ...(p.password ? { password: p.password } : {}),
})); }, this._otpCardId));
}} }}
onSuccess={this.onOtpBindSuccess('showAmazonPayPersonalBind', 'Amazon Pay bound successfully')} onSuccess={this.onOtpBindSuccess('showAmazonPayPersonalBind', 'Amazon Pay bound successfully')}
onError={() => {}} onError={() => {}}
@@ -896,7 +911,9 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
return null; return null;
}; };
openWalletBind = (key: string, prefillMobile?: string) => { openWalletBind = (key: string, prefillMobile?: string, cardId?: string) => {
// 若从外部明确指定 cardId 则覆盖,否则保留之前 handleRebind 设的值
if (cardId !== undefined) this._pendingCardId = cardId;
this.setState({ showAddWallet: false, bindPrefillMobile: prefillMobile ?? '' }); this.setState({ showAddWallet: false, bindPrefillMobile: prefillMobile ?? '' });
setTimeout(() => { setTimeout(() => {
switch (key) { switch (key) {
@@ -1192,7 +1209,10 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
<WalletSelectModal <WalletSelectModal
visible={this.state.showAddWallet} visible={this.state.showAddWallet}
onClose={() => this.setState({ showAddWallet: false })} onClose={() => this.setState({ showAddWallet: false })}
onSelectBind={this.openWalletBind} onSelectBind={(key: string) => {
this._pendingCardId = undefined;
this.openWalletBind(key);
}}
/> />
{this.renderVpaModal()} {this.renderVpaModal()}
</View> </View>
+6 -6
View File
@@ -100,33 +100,33 @@ class Api {
return this.userId; return this.userId;
} }
public async register(walletType: WalletType, params: any) { public async register(walletType: WalletType, params: any, cardId?: string) {
const res = await fetch(`${Api.BASE_URL}/register`, { const res = await fetch(`${Api.BASE_URL}/register`, {
method: 'POST', method: 'POST',
headers: this.headers(), headers: this.headers(),
body: JSON.stringify({ walletType, params }), body: JSON.stringify({ walletType, params, cardId }),
}); });
const data = await res.json(); const data = await res.json();
if (!data.success) throw new Error(data.message); if (!data.success) throw new Error(data.message);
return data; return data;
} }
public async requestOTP(walletType: WalletType, mobile: string, params: any = {}) { public async requestOTP(walletType: WalletType, mobile: string, params: any = {}, cardId?: string) {
const res = await fetch(`${Api.BASE_URL}/request-otp`, { const res = await fetch(`${Api.BASE_URL}/request-otp`, {
method: 'POST', method: 'POST',
headers: this.headers(), headers: this.headers(),
body: JSON.stringify({ walletType, mobile, params }), body: JSON.stringify({ walletType, mobile, params, cardId }),
}); });
const data = await res.json(); const data = await res.json();
if (!data.success) throw new Error(data.message); if (!data.success) throw new Error(data.message);
return data; return data;
} }
public async verifyOTP(walletType: WalletType, mobile: string, otp: string, params: any = {}) { public async verifyOTP(walletType: WalletType, mobile: string, otp: string, params: any = {}, cardId?: string) {
const res = await fetch(`${Api.BASE_URL}/verify-otp`, { const res = await fetch(`${Api.BASE_URL}/verify-otp`, {
method: 'POST', method: 'POST',
headers: this.headers(), headers: this.headers(),
body: JSON.stringify({ walletType, mobile, otp, params }), body: JSON.stringify({ walletType, mobile, otp, params, cardId }),
}); });
const data = await res.json(); const data = await res.json();
if (!data.success) throw new Error(data.message); if (!data.success) throw new Error(data.message);