Skip to content
This repository was archived by the owner on Jun 20, 2024. It is now read-only.

Commit bf03cb6

Browse files
committed
add web3 sign in
1 parent 953f26e commit bf03cb6

File tree

12 files changed

+611
-17
lines changed

12 files changed

+611
-17
lines changed

app/build.gradle

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
apply plugin: 'com.android.application'
2+
apply plugin: 'kotlin-android'
23

34
android {
45
compileSdkVersion 32
@@ -44,7 +45,7 @@ dependencies {
4445

4546
implementation 'com.google.android.gms:play-services-auth:20.1.0'
4647
implementation 'com.google.android.material:material:1.5.0'
47-
implementation 'com.jakewharton.timber:timber:5.0.1'
48+
implementation 'com.jakewharton.timber:timber:4.7.1'
4849

4950
// AndroidX
5051
implementation 'androidx.appcompat:appcompat:1.2.0'
@@ -58,9 +59,23 @@ dependencies {
5859
// Retrofit
5960
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
6061
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
62+
implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
6163

6264
testImplementation 'junit:junit:4.13.2'
6365

6466
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
6567
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
68+
69+
// Web3
70+
implementation 'com.github.mobilekosmos:kotlin-walletconnect-lib:0.9.9.8'
71+
implementation 'org.java-websocket:Java-WebSocket:1.5.3'
72+
implementation ('org.web3j:core:4.8.7-android'){
73+
exclude group: 'org.bouncycastle', module: '*'
74+
}
75+
76+
//1.5.0 is currently the latest stable version of AndroidX Core for Kotlin.
77+
//If you already have "androidx.core:core" implemented, remove it.
78+
implementation 'androidx.core:core-ktx:1.5.+'
79+
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.5.+'
6680
}
81+

app/src/main/AndroidManifest.xml

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
34
package="ai.elimu.crowdsource">
45

56
<uses-permission android:name="android.permission.INTERNET" />
@@ -11,8 +12,10 @@
1112
android:icon="@mipmap/ic_launcher"
1213
android:label="@string/app_name"
1314
android:roundIcon="@mipmap/ic_launcher_round"
15+
android:networkSecurityConfig="@xml/network_config"
1416
android:supportsRtl="true"
15-
android:theme="@style/AppTheme">
17+
android:theme="@style/AppTheme"
18+
tools:targetApi="n">
1619

1720
<activity android:name=".MainActivity"
1821
android:exported="true">
@@ -24,7 +27,7 @@
2427

2528
<activity android:name=".ui.language.SelectLanguageActivity" />
2629

27-
<activity android:name=".ui.authentication.SignInWithGoogleActivity" />
30+
<activity android:name=".ui.authentication.SignInActivity" />
2831

2932
<activity android:name=".ui.BottomNavigationActivity" />
3033

app/src/main/java/ai/elimu/crowdsource/BaseApplication.java

+145-3
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,57 @@
33
import android.app.Application;
44
import android.util.Log;
55

6+
import androidx.annotation.NonNull;
7+
8+
import com.squareup.moshi.Moshi;
9+
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory;
10+
11+
import org.jetbrains.annotations.NotNull;
12+
import org.walletconnect.Session;
13+
import org.walletconnect.impls.FileWCSessionStore;
14+
import org.walletconnect.impls.MoshiPayloadAdapter;
15+
import org.walletconnect.impls.OkHttpTransport;
16+
import org.walletconnect.impls.WCSession;
17+
import org.walletconnect.impls.WCSessionStore;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.util.Collections;
22+
import java.util.Random;
23+
import java.util.UUID;
24+
25+
import ai.elimu.crowdsource.server.BridgeServer;
626
import ai.elimu.crowdsource.util.SharedPreferencesHelper;
727
import ai.elimu.crowdsource.util.VersionHelper;
828
import ai.elimu.model.v2.enums.Language;
29+
import kotlin.Unit;
30+
import kotlin.jvm.functions.Function1;
31+
import kotlin.jvm.internal.Intrinsics;
32+
import okhttp3.OkHttpClient;
933
import retrofit2.Retrofit;
1034
import retrofit2.converter.gson.GsonConverterFactory;
1135
import timber.log.Timber;
1236

1337
public class BaseApplication extends Application {
38+
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
39+
public static Session.FullyQualifiedConfig config;
40+
public static Session session;
41+
private static OkHttpClient client;
42+
private static Moshi moshi;
43+
private static BridgeServer bridge;
44+
private static WCSessionStore storage;
1445

1546
@Override
1647
public void onCreate() {
1748
Log.i(getClass().getName(), "onCreate");
1849
super.onCreate();
19-
20-
// Log config
21-
Timber.plant(new Timber.DebugTree());
50+
this.initMoshi();
51+
this.initClient();
52+
this.initBridge();
53+
this.initSessionStorage();
54+
if(BuildConfig.DEBUG){
55+
Timber.plant(new Timber.DebugTree());
56+
}
2257
Timber.i("onCreate");
2358

2459
VersionHelper.updateAppVersion(getApplicationContext());
@@ -48,4 +83,111 @@ public String getBaseUrl() {
4883
public String getRestUrl() {
4984
return getBaseUrl() + "/rest/v2";
5085
}
86+
87+
88+
private void initClient() {
89+
OkHttpClient var10000 = (new OkHttpClient.Builder()).build();
90+
Intrinsics.checkNotNullExpressionValue(var10000, "OkHttpClient.Builder().build()");
91+
client = var10000;
92+
}
93+
94+
private void initMoshi() {
95+
Moshi var10000 = (new com.squareup.moshi.Moshi.Builder()).add(new KotlinJsonAdapterFactory()).build();
96+
Intrinsics.checkNotNullExpressionValue(var10000, "Moshi.Builder().build()");
97+
moshi = var10000;
98+
}
99+
100+
private void initBridge() {
101+
bridge = new BridgeServer(moshi);
102+
bridge.start();
103+
}
104+
105+
private void initSessionStorage() {
106+
File tmp = new File(this.getCacheDir(), "session_store.json");
107+
try {
108+
tmp.createNewFile();
109+
storage = new FileWCSessionStore(tmp, moshi);
110+
} catch (IOException e) {
111+
e.printStackTrace();
112+
}
113+
114+
}
115+
116+
117+
@NotNull
118+
public static String bytesToHex(byte[] bytes) {
119+
Intrinsics.checkNotNullParameter(bytes, "bytes");
120+
char[] hexChars = new char[bytes.length * 2];
121+
int j = 0;
122+
123+
for (int var4 = bytes.length; j < var4; ++j) {
124+
int v = bytes[j] & 255;
125+
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
126+
hexChars[j * 2 + 1] = HEX_ARRAY[v & 15];
127+
}
128+
129+
return new String(hexChars);
130+
}
131+
132+
public static void resetSession() throws Exception {
133+
if (session != null) {
134+
session.clearCallbacks();
135+
}
136+
byte[] randomBytes = new byte[32];
137+
Random r = new Random();
138+
r.nextBytes(randomBytes);
139+
String key = bytesToHex(randomBytes);
140+
String uuid = UUID.randomUUID().toString();
141+
config = new Session.FullyQualifiedConfig(uuid, "http://localhost:" + BridgeServer.Companion.getPORT(), key, "wc", 1);
142+
// config = new Session.FullyQualifiedConfig(uuid, "https://bridge.walletconnect.org", "f70af2060965927b7e709503e71b3cbf", "wc", 1);
143+
session = new WCSession(
144+
config,
145+
new WrappedMoshiPayloadAdapter(new MoshiPayloadAdapter(moshi)),
146+
storage,
147+
new WrappedOkHttpTransportBuilder(new OkHttpTransport.Builder(client, moshi)),
148+
new Session.PeerMeta(
149+
"elimu.ai",
150+
"Elimu Crowdsource App",
151+
"Elimu Crowdsource App",
152+
Collections.emptyList()
153+
),
154+
null,
155+
null
156+
);
157+
session.offer();
158+
}
159+
160+
static class WrappedMoshiPayloadAdapter implements Session.PayloadAdapter {
161+
private final MoshiPayloadAdapter wrapped;
162+
163+
public WrappedMoshiPayloadAdapter(MoshiPayloadAdapter wrapped) {
164+
this.wrapped = wrapped;
165+
}
166+
167+
@NonNull
168+
@Override
169+
public Session.MethodCall parse(@NonNull String s, @NonNull String s1) {
170+
return this.wrapped.parse(s, s1);
171+
}
172+
173+
@NonNull
174+
@Override
175+
public String prepare(@NonNull Session.MethodCall methodCall, @NonNull String s) {
176+
return this.wrapped.prepare(methodCall, s);
177+
}
178+
}
179+
180+
static class WrappedOkHttpTransportBuilder implements Session.Transport.Builder {
181+
private final OkHttpTransport.Builder wrapped;
182+
183+
public WrappedOkHttpTransportBuilder(OkHttpTransport.Builder wrapped) {
184+
this.wrapped = wrapped;
185+
}
186+
187+
@NonNull
188+
@Override
189+
public Session.Transport build(@NonNull String s, @NonNull Function1<? super Session.Transport.Status, Unit> function1, @NonNull Function1<? super Session.Transport.Message, Unit> function11) {
190+
return this.wrapped.build(s, function1, function11);
191+
}
192+
}
51193
}

app/src/main/java/ai/elimu/crowdsource/MainActivity.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import androidx.appcompat.app.AppCompatActivity;
88

9-
import ai.elimu.crowdsource.ui.authentication.SignInWithGoogleActivity;
9+
import ai.elimu.crowdsource.ui.authentication.SignInActivity;
1010
import ai.elimu.crowdsource.ui.language.SelectLanguageActivity;
1111
import ai.elimu.crowdsource.ui.BottomNavigationActivity;
1212
import ai.elimu.crowdsource.util.SharedPreferencesHelper;
@@ -42,7 +42,7 @@ protected void onStart() {
4242
Timber.i("providerIdGoogle: " + providerIdGoogle);
4343
if (TextUtils.isEmpty(providerIdGoogle)) {
4444
// Redirect to sign-in with Google
45-
Intent signInWithGoogleIntent = new Intent(getApplicationContext(), SignInWithGoogleActivity.class);
45+
Intent signInWithGoogleIntent = new Intent(getApplicationContext(), SignInActivity.class);
4646
startActivity(signInWithGoogleIntent);
4747
finish();
4848
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package ai.elimu.crowdsource.server
2+
3+
import android.util.Log
4+
import com.squareup.moshi.Moshi
5+
import com.squareup.moshi.Types
6+
import org.java_websocket.WebSocket
7+
import org.java_websocket.handshake.ClientHandshake
8+
import org.java_websocket.server.WebSocketServer
9+
import java.lang.Exception
10+
import java.lang.ref.WeakReference
11+
import java.net.InetSocketAddress
12+
import java.util.*
13+
import java.util.concurrent.ConcurrentHashMap
14+
15+
class BridgeServer(moshi: Moshi) : WebSocketServer(InetSocketAddress(PORT)) {
16+
17+
private val adapter = moshi.adapter<Map<String, Any>>(
18+
Types.newParameterizedType(
19+
Map::class.java,
20+
String::class.java,
21+
Any::class.java
22+
)
23+
)
24+
25+
private val pubs: MutableMap<String, MutableList<WeakReference<WebSocket>>> = ConcurrentHashMap()
26+
private val pubsLock = Any()
27+
private val pubsCache: MutableMap<String, String?> = ConcurrentHashMap()
28+
29+
override fun onOpen(conn: WebSocket?, handshake: ClientHandshake?) {
30+
Log.d("#####", "onOpen: ${conn?.remoteSocketAddress?.address?.hostAddress}")
31+
}
32+
33+
override fun onClose(conn: WebSocket?, code: Int, reason: String?, remote: Boolean) {
34+
Log.d("#####", "onClose: ${conn?.remoteSocketAddress?.address?.hostAddress}")
35+
conn?.let { cleanUpSocket(it) }
36+
}
37+
38+
override fun onMessage(conn: WebSocket?, message: String?) {
39+
Log.d("#####", "Message: $message")
40+
try {
41+
conn ?: error("Unknown socket")
42+
message?.also {
43+
val msg = adapter.fromJson(it) ?: error("Invalid message")
44+
val type: String = msg["type"] as String? ?: error("Type not found")
45+
val topic: String = msg["topic"] as String? ?: error("Topic not found")
46+
when (type) {
47+
"pub" -> {
48+
var sendMessage = false
49+
pubs[topic]?.forEach { r ->
50+
r.get()?.apply {
51+
send(message)
52+
sendMessage = true
53+
}
54+
}
55+
if (!sendMessage) {
56+
Log.d("#####", "Cache message: $message")
57+
pubsCache[topic] = message
58+
}
59+
}
60+
"sub" -> {
61+
pubs.getOrPut(topic, { mutableListOf() }).add(WeakReference(conn))
62+
pubsCache[topic]?.let { cached ->
63+
Log.d("#####", "Send cached: $cached")
64+
conn.send(cached)
65+
}
66+
}
67+
"ack" -> {
68+
69+
}
70+
else -> error("Unknown type")
71+
}
72+
}
73+
} catch (e: Exception) {
74+
e.printStackTrace()
75+
}
76+
}
77+
78+
override fun onStart() {
79+
Log.d("#####", "Server started")
80+
connectionLostTimeout = 0
81+
}
82+
83+
override fun onError(conn: WebSocket?, ex: Exception?) {
84+
Log.d("#####", "onError")
85+
ex?.printStackTrace()
86+
conn?.let { cleanUpSocket(it) }
87+
}
88+
89+
private fun cleanUpSocket(conn: WebSocket) {
90+
synchronized(pubsLock) {
91+
pubs.forEach {
92+
it.value.removeAll { r -> r.get().let { v -> v == null || v == conn } }
93+
}
94+
}
95+
}
96+
97+
companion object {
98+
val PORT = 5000 + Random().nextInt(60000)
99+
}
100+
}

0 commit comments

Comments
 (0)