From 6f15a6007a43d46cb6dc667f2b20378f14453d84 Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 25 Mar 2024 16:55:46 +1100 Subject: [PATCH] - Fix for WalletConnect V2 Foreground Service (#3367) --- app/src/main/java/com/alphawallet/app/C.java | 2 + .../app/interact/WalletConnectInteract.java | 21 ++-- .../app/service/WalletConnectV2Service.java | 44 ++++--- .../com/alphawallet/app/ui/HomeActivity.java | 16 ++- .../alphawallet/app/ui/WalletFragment.java | 4 +- .../app/ui/widget/adapter/TokensAdapter.java | 9 +- .../java/com/alphawallet/app/util/Utils.java | 14 ++- .../app/viewmodel/TokenFunctionViewModel.java | 2 +- .../walletconnect/AWWalletConnectClient.java | 114 +++++++----------- .../com/alphawallet/app/widget/TokenIcon.java | 5 - 10 files changed, 113 insertions(+), 118 deletions(-) diff --git a/app/src/main/java/com/alphawallet/app/C.java b/app/src/main/java/com/alphawallet/app/C.java index 505a08818..33b313619 100644 --- a/app/src/main/java/com/alphawallet/app/C.java +++ b/app/src/main/java/com/alphawallet/app/C.java @@ -211,6 +211,7 @@ public abstract class C { public static final String APP_FOREGROUND_STATE = "com.alphawallet.APP_FOREGROUND_STATE"; public static final String EXTRA_APP_FOREGROUND = "com.alphawallet.IS_FOREGORUND"; public static final String QRCODE_SCAN = "com.alphawallet.QRSCAN"; + public static final String AWALLET_CODE = "com.alphawallet.AWALLET"; public static final String SIGNAL_NFT_SYNC = "com.alphawallet.SYNC_NFT"; public static final String SYNC_STATUS = "com.alphawallet.SYNC_STATUS"; @@ -278,6 +279,7 @@ public abstract class C { public static final String DAPP_SUFFIX_RECEIVE = "receive"; public static final String DAPP_PREFIX_MAPS = "maps.google.com/maps?daddr="; public static final String DAPP_PREFIX_WALLETCONNECT = "wc"; + public static final String DAPP_PREFIX_AWALLET = "awallet"; public static final String ENS_SCAN_BLOCK = "ens_check_block"; public static final String ENS_HISTORY = "ensHistory"; diff --git a/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java b/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java index 55e75aae6..dda05ee36 100644 --- a/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java +++ b/app/src/main/java/com/alphawallet/app/interact/WalletConnectInteract.java @@ -2,7 +2,6 @@ package com.alphawallet.app.interact; import com.alphawallet.app.entity.walletconnect.WalletConnectSessionItem; import com.alphawallet.app.entity.walletconnect.WalletConnectV2SessionItem; -import com.alphawallet.app.service.RealmManager; import com.walletconnect.web3.wallet.client.Wallet; import com.walletconnect.web3.wallet.client.Web3Wallet; @@ -15,12 +14,10 @@ import timber.log.Timber; public class WalletConnectInteract { - private final RealmManager realmManager; - @Inject - public WalletConnectInteract(RealmManager realmManager) + public WalletConnectInteract() { - this.realmManager = realmManager; + } public int getSessionsCount() @@ -30,8 +27,7 @@ public class WalletConnectInteract public List getSessions() { - List result = new ArrayList<>(); - result.addAll(getWalletConnectV2SessionItems()); + List result = new ArrayList<>(getWalletConnectV2SessionItems()); //now sort for active/newness result.sort((l, r) -> Long.compare(r.expiryTime, l.expiryTime)); @@ -39,6 +35,17 @@ public class WalletConnectInteract return result; } + public void fetchSessions(SessionFetchCallback sessionFetchCallback) + { + fetch(sessionFetchCallback); + } + + private void fetch(SessionFetchCallback sessionFetchCallback) + { + List result = new ArrayList<>(getWalletConnectV2SessionItems()); + sessionFetchCallback.onFetched(result); + } + private List getWalletConnectV2SessionItems() { List result = new ArrayList<>(); diff --git a/app/src/main/java/com/alphawallet/app/service/WalletConnectV2Service.java b/app/src/main/java/com/alphawallet/app/service/WalletConnectV2Service.java index f9741f7c7..10855adb1 100644 --- a/app/src/main/java/com/alphawallet/app/service/WalletConnectV2Service.java +++ b/app/src/main/java/com/alphawallet/app/service/WalletConnectV2Service.java @@ -5,7 +5,6 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; -import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; @@ -21,6 +20,9 @@ import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint public class WalletConnectV2Service extends Service { + private static final String TAG = WalletConnectV2Service.class.getName(); + + final String CHANNEL_ID = "WalletConnectV2Service"; @Override public IBinder onBind(Intent intent) { @@ -32,32 +34,38 @@ public class WalletConnectV2Service extends Service public void onCreate() { super.onCreate(); - String CHANNEL_ID = "my_channel_01"; - NotificationChannel channel = new NotificationChannel(CHANNEL_ID, - "WalletConnect V2", - NotificationManager.IMPORTANCE_DEFAULT); + } - NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.createNotificationChannel(channel); + private Notification createNotification() + { + Intent notificationIntent = new Intent(this, WalletConnectNotificationActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE); - Intent intent = new Intent(getApplicationContext(), WalletConnectNotificationActivity.class); - PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE); - Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_logo) - .setOnlyAlertOnce(true) + return new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle(getString(R.string.notify_wallet_connect_title)) .setContentText(getString(R.string.notify_wallet_connect_content)) + .setSmallIcon(R.drawable.ic_logo) .setContentIntent(pendingIntent) .build(); - - startForeground(1, notification); - notificationManager.notify(1, notification); } - @Override - public int onStartCommand(Intent intent, int flags, int startId) + @RequiresApi(api = Build.VERSION_CODES.O) + private void createNotificationChannel() { - return super.onStartCommand(intent, flags, startId); + CharSequence name = getString(R.string.notify_wallet_connect_title); + String description = getString(R.string.notify_wallet_connect_content); + NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(description); + NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + @RequiresApi(api = Build.VERSION_CODES.O) + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + createNotificationChannel(); + Notification notification = createNotification(); + startForeground(1, notification); + return START_STICKY; } @Override diff --git a/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java b/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java index d4d65d191..b4fb77ee1 100644 --- a/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/HomeActivity.java @@ -319,7 +319,7 @@ public class HomeActivity extends BaseNavigationActivity implements View.OnClick if (data != null) { - checkIntents(data.toString(), intent); + handleDeeplink(data.toString(), intent); } Intent i = new Intent(this, PriceAlertsService.class); @@ -453,6 +453,13 @@ public class HomeActivity extends BaseNavigationActivity implements View.OnClick hideDialog(); qrCodeScanner.launch(options); }); + + getSupportFragmentManager() + .setFragmentResultListener(C.AWALLET_CODE, this, (requestKey, b) -> + { + String code = b.getString(C.AWALLET_CODE); + handleDeeplink(code, null); + }); } //TODO: Implement all QR scan using this method @@ -476,7 +483,7 @@ public class HomeActivity extends BaseNavigationActivity implements View.OnClick if (data != null) { - checkIntents(data.toString(), startIntent); + handleDeeplink(data.toString(), startIntent); } } @@ -603,7 +610,7 @@ public class HomeActivity extends BaseNavigationActivity implements View.OnClick } @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) + protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); int oldPage = savedInstanceState.getInt(STORED_PAGE); @@ -1095,9 +1102,8 @@ public class HomeActivity extends BaseNavigationActivity implements View.OnClick inset.show(WindowInsetsCompat.Type.statusBars() | WindowInsetsCompat.Type.navigationBars()); } - private void checkIntents(String importData, Intent startIntent) + private void handleDeeplink(String importData, Intent startIntent) { - //decode deeplink and handle DeepLinkRequest request = DeepLinkService.parseIntent(importData, startIntent); switch (request.type) { diff --git a/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java b/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java index 520238d1f..ba45d3bf1 100644 --- a/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java +++ b/app/src/main/java/com/alphawallet/app/ui/WalletFragment.java @@ -118,6 +118,7 @@ public class WalletFragment extends BaseFragment implements private ActivityResultLauncher handleBackupClick; private ActivityResultLauncher tokenManagementLauncher; private boolean completed = false; + private boolean hasWCSession = false; @Inject AWWalletConnectClient awWalletConnectClient; @@ -270,6 +271,7 @@ public class WalletFragment extends BaseFragment implements viewModel.removeDisplayTokens().observe(getViewLifecycleOwner(), this::removeTokens); viewModel.getTokensService().startWalletSync(this); viewModel.activeWalletConnectSessions().observe(getViewLifecycleOwner(), walletConnectSessionItems -> { + hasWCSession = !walletConnectSessionItems.isEmpty(); adapter.showActiveWalletConnectSessions(walletConnectSessionItems); }); } @@ -828,7 +830,7 @@ public class WalletFragment extends BaseFragment implements @Override public boolean hasWCSession() { - return awWalletConnectClient != null && awWalletConnectClient.hasWalletConnectSessions(); + return hasWCSession || (awWalletConnectClient != null && awWalletConnectClient.hasWalletConnectSessions()); } @Override diff --git a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java index 391544f0a..59771e20e 100644 --- a/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java +++ b/app/src/main/java/com/alphawallet/app/ui/widget/adapter/TokensAdapter.java @@ -623,14 +623,7 @@ public class TokensAdapter extends RecyclerView.Adapter public void showActiveWalletConnectSessions(List sessions) { - if (sessions.isEmpty()) - { - removeItem(WalletConnectSessionHolder.VIEW_TYPE); - } - else - { - items.add(new WalletConnectSessionSortedItem(sessions, 2)); - } + checkWalletConnect(); } public void removeItem(int viewType) diff --git a/app/src/main/java/com/alphawallet/app/util/Utils.java b/app/src/main/java/com/alphawallet/app/util/Utils.java index 1a40ca344..5a136bcdc 100644 --- a/app/src/main/java/com/alphawallet/app/util/Utils.java +++ b/app/src/main/java/com/alphawallet/app/util/Utils.java @@ -73,7 +73,6 @@ import java.net.URLDecoder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.text.DateFormat; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -81,7 +80,6 @@ import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -115,7 +113,7 @@ public class Utils public static String formatUrl(String url) { - if (URLUtil.isHttpsUrl(url) || URLUtil.isHttpUrl(url)) + if (URLUtil.isHttpsUrl(url) || URLUtil.isHttpUrl(url) || isWalletPrefix(url)) { return url; } @@ -132,6 +130,16 @@ public class Utils } } + public static boolean isWalletPrefix(String url) + { + return url.startsWith(C.DAPP_PREFIX_TELEPHONE) || + url.startsWith(C.DAPP_PREFIX_MAILTO) || + url.startsWith(C.DAPP_PREFIX_ALPHAWALLET) || + url.startsWith(C.DAPP_PREFIX_MAPS) || + url.startsWith(C.DAPP_PREFIX_WALLETCONNECT) || + url.startsWith(C.DAPP_PREFIX_AWALLET); + } + public static boolean isValidUrl(String url) { if (TextUtils.isEmpty(url)) return false; diff --git a/app/src/main/java/com/alphawallet/app/viewmodel/TokenFunctionViewModel.java b/app/src/main/java/com/alphawallet/app/viewmodel/TokenFunctionViewModel.java index d4c877daa..a0d357f6e 100644 --- a/app/src/main/java/com/alphawallet/app/viewmodel/TokenFunctionViewModel.java +++ b/app/src/main/java/com/alphawallet/app/viewmodel/TokenFunctionViewModel.java @@ -589,7 +589,7 @@ public class TokenFunctionViewModel extends BaseViewModel implements Transaction public void checkForNewScript(Token token) { if (token == null) return; - //check server for new tokenscript + //check server for new TokenScript scriptUpdate = assetDefinitionService.checkServerForScript(token, scriptUpdateInProgress) .observeOn(Schedulers.io()) .subscribeOn(Schedulers.single()) diff --git a/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java b/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java index e30689d8d..ab8cc6d95 100644 --- a/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java +++ b/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java @@ -17,6 +17,7 @@ import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import androidx.lifecycle.MutableLiveData; import com.alphawallet.app.App; @@ -83,7 +84,7 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate private ActionSheetCallback actionSheetCallback; private boolean hasConnection; private Application application; - private PreferenceRepositoryType preferenceRepository; + private final PreferenceRepositoryType preferenceRepository; public AWWalletConnectClient(Context context, WalletConnectInteract walletConnectInteract, PreferenceRepositoryType preferenceRepository, GasService gasService) { @@ -94,23 +95,9 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate hasConnection = false; } - /*public void onSessionDelete(@NonNull Model.SessionDelete deletedSession) + public void onSessionDelete(@NonNull Model.SessionDelete deletedSession) { - updateNotification(); - }*/ - - public void onSessionProposal(@NonNull Model.SessionProposal sessionProposal) - { - WalletConnectV2SessionItem sessionItem = WalletConnectV2SessionItem.from(sessionProposal); - if (!validChainId(sessionItem.chains)) - { - return; - } - AWWalletConnectClient.sessionProposal = sessionProposal; - Intent intent = new Intent(context, WalletConnectV2Activity.class); - intent.putExtra("session", sessionItem); - intent.setFlags(FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + updateNotification(null); } public boolean hasWalletConnectSessions() @@ -135,36 +122,6 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate return true; } - public void onSessionRequest(@NonNull Model.SessionRequest sessionRequest) - { - String checkMethod; - String method = sessionRequest.getRequest().getMethod(); - if (method.startsWith("eth_signTypedData")) - { - checkMethod = "eth_signTypedData"; - } - else - { - checkMethod = method; - } - - if (!WCMethodChecker.includes(checkMethod)) - { - reject(sessionRequest); - return; - } - - Model.Session settledSession = getSession(sessionRequest.getTopic()); - - Activity topActivity = App.getInstance().getTopActivity(); - if (topActivity != null) - { - WalletConnectV2SessionRequestHandler handler = new WalletConnectV2SessionRequestHandler(sessionRequest, settledSession, topActivity, this); - handler.handle(method, actionSheetCallback); - requestHandlers.append(sessionRequest.getRequest().getId(), handler); - } - } - private Session getSession(String topic) { List listOfSettledSessions; @@ -223,7 +180,7 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate Params.SessionApprove approve = new Params.SessionApprove(proposerPublicKey, buildNamespaces(sessionProposal, selectedAccounts), sessionProposal.getRelayProtocol()); Web3Wallet.INSTANCE.approveSession(approve, sessionApprove -> { new Handler(Looper.getMainLooper()).postDelayed(() -> { - //updateNotification(); + updateNotification(sessionProposal); callback.onSessionProposalApproved(); }, 500); return null; @@ -295,18 +252,35 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate return sessionItemMutableLiveData; } - private void updateService(Context context, List walletConnectSessionItems) + public void updateNotification(Model.SessionProposal sessionProposal) { - try - { - if (walletConnectSessionItems.isEmpty()) + walletConnectInteract.fetchSessions(items -> { + if (sessionProposal != null && items.isEmpty()) { - context.stopService(new Intent(context, WalletConnectV2Service.class)); + items.add(WalletConnectV2SessionItem.from(sessionProposal)); } - else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + + updateService(items); + sessionItemMutableLiveData.postValue(items); + }); + } + + private void updateService(List items) + { + try + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Intent service = new Intent(context, WalletConnectV2Service.class); - context.startForegroundService(service); + if (items.isEmpty()) + { + context.stopService(new Intent(context, WalletConnectV2Service.class)); + //now signal + } + else + { + Intent serviceIntent = new Intent(context, WalletConnectV2Service.class); + ContextCompat.startForegroundService(context, serviceIntent); + } } } catch (Exception e) @@ -319,7 +293,6 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate public void reject(Model.SessionProposal sessionProposal, WalletConnectV2Callback callback) { - Web3Wallet.INSTANCE.rejectSession( new Params.SessionReject(sessionProposal.getProposerPublicKey(), context.getString(R.string.message_reject_request)), sessionReject -> null, @@ -363,8 +336,19 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate this.actionSheetCallback = actionSheetCallback; } + public String getRelayServer() + { + return String.format("%s/?projectId=%s", C.WALLET_CONNECT_REACT_APP_RELAY_URL, keyProvider.getWalletConnectProjectId()); + } + public void init(Application application) { + if (keyProvider.getWalletConnectProjectId().isEmpty()) + { + //Early return for no wallet connect + return; + } + this.application = application; Core.Model.AppMetaData appMetaData = getAppMetaData(application); String relayServer = String.format("%s/?projectId=%s", C.WALLET_CONNECT_REACT_APP_RELAY_URL, keyProvider.getWalletConnectProjectId()); @@ -386,6 +370,8 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate try { Web3Wallet.INSTANCE.setWalletDelegate(this); + //ensure notification is displayed if session is active + updateNotification(null); } catch (Exception e) { @@ -394,7 +380,7 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate } @NonNull - private Core.Model.AppMetaData getAppMetaData(Application application) + public Core.Model.AppMetaData getAppMetaData(Application application) { String name = application.getString(R.string.app_name); String url = C.ALPHAWALLET_WEBSITE; @@ -466,12 +452,6 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate } } - /*@Override - public void onAuthRequest(@NonNull Model.AuthRequest authRequest) - { - showApprovalDialog(authRequest); - }*/ - private void showApprovalDialog(Model.AuthRequest authRequest) { String activeWallet = preferenceRepository.getCurrentWalletAddress(); @@ -664,12 +644,6 @@ public class AWWalletConnectClient implements Web3Wallet.WalletDelegate } } - @Override - public void onSessionDelete(@NonNull Model.SessionDelete sessionDelete) - { - - } - public Intent getSessionIntent(Context appContext) { Intent intent; diff --git a/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java b/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java index c2752b29c..837cd8e92 100644 --- a/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java +++ b/app/src/main/java/com/alphawallet/app/widget/TokenIcon.java @@ -265,11 +265,6 @@ public class TokenIcon extends ConstraintLayout { setupDefaultIcon(); - if (token.tokenInfo.address.equalsIgnoreCase("0xfaafdc07907ff5120a76b34b731b278c38d6043c")) - { - System.out.println("YOLESS"); - } - final RequestOptions optionalCircleCrop = squareToken || iconUrl.startsWith(ALPHAWALLET_REPO_NAME) ? new RequestOptions() : new RequestOptions().circleCrop(); currentRq = Glide.with(this)