Skip to content

Commit 00b7af3

Browse files
committed
feat(app): expose F-Droid autogen bridge
1 parent 416075e commit 00b7af3

10 files changed

Lines changed: 515 additions & 35 deletions

File tree

app_flutter/android/app/src/main/kotlin/net/xzos/upgradeall/GetterBridgeRequestBuilder.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,40 @@
11
package net.xzos.upgradeall
22

3+
import org.json.JSONArray
34
import org.json.JSONObject
45

56
object GetterBridgeRequestBuilder {
67
fun readOperationRequest(args: Map<*, *>): String = operationRequest(args)
78

89
fun runtimeOperationRequest(args: Map<*, *>): String = operationRequest(args)
910

11+
fun fdroidAutogenPreviewRequest(args: Map<*, *>): String {
12+
val payload = args["payload"] as? Map<*, *> ?: emptyMap<Any?, Any?>()
13+
return JSONObject()
14+
.put("payload", JSONObject(payload))
15+
.toString()
16+
}
17+
18+
fun autogenApplyRequest(args: Map<*, *>): String {
19+
val previewJson = args["preview_json"] as? String
20+
?: throw IllegalArgumentException("preview_json is required")
21+
val acceptance = args["acceptance"] as? Map<*, *>
22+
val packageIds = acceptance
23+
?.get("package_ids")
24+
?.let { value -> value as? Collection<*> }
25+
?.map { value -> value.toString() }
26+
?: emptyList<String>()
27+
return JSONObject()
28+
.put("preview", JSONObject(previewJson))
29+
.put(
30+
"acceptance",
31+
JSONObject()
32+
.put("mode", acceptance?.get("mode") as? String ?: "all")
33+
.put("package_ids", JSONArray(packageIds)),
34+
)
35+
.toString()
36+
}
37+
1038
private fun operationRequest(args: Map<*, *>): String {
1139
val operation = args["operation"] as? String
1240
?: throw IllegalArgumentException("operation is required")

app_flutter/android/app/src/main/kotlin/net/xzos/upgradeall/MainActivity.kt

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import io.flutter.plugin.common.MethodChannel
1010
import java.io.File
1111
import java.util.concurrent.Executors
1212
import net.xzos.upgradeall.getter.NativeLib
13-
import org.json.JSONArray
1413
import org.json.JSONObject
1514

1615
class MainActivity : FlutterActivity() {
@@ -59,6 +58,14 @@ class MainActivity : FlutterActivity() {
5958
nativeLib.applyInstalledAutogen(applyInstalledAutogenRequest(call))
6059
}
6160

61+
"previewFdroidAutogen" -> runGetterBridge(result) {
62+
nativeLib.previewFdroidAutogen(previewFdroidAutogenRequest(call))
63+
}
64+
65+
"applyFdroidAutogen" -> runGetterBridge(result) {
66+
nativeLib.applyFdroidAutogen(applyFdroidAutogenRequest(call))
67+
}
68+
6269
"importLegacyRoomDatabase" -> runGetterBridge(result) {
6370
nativeLib.importLegacyRoomDatabase(importLegacyRoomDatabaseRequest(call))
6471
}
@@ -171,23 +178,22 @@ class MainActivity : FlutterActivity() {
171178

172179
private fun applyInstalledAutogenRequest(call: MethodCall): String {
173180
val args = call.arguments as? Map<*, *> ?: emptyMap<Any?, Any?>()
174-
val previewJson = args["preview_json"] as? String
175-
?: throw IllegalArgumentException("preview_json is required")
176-
val acceptance = args["acceptance"] as? Map<*, *>
177-
val packageIds = acceptance
178-
?.get("package_ids")
179-
?.let { value -> value as? Collection<*> }
180-
?.map { value -> value.toString() }
181-
?: emptyList<String>()
182-
return JSONObject()
181+
return JSONObject(GetterBridgeRequestBuilder.autogenApplyRequest(args))
182+
.put("data_dir", getterDataDir().absolutePath)
183+
.toString()
184+
}
185+
186+
private fun previewFdroidAutogenRequest(call: MethodCall): String {
187+
val args = call.arguments as? Map<*, *> ?: emptyMap<Any?, Any?>()
188+
return JSONObject(GetterBridgeRequestBuilder.fdroidAutogenPreviewRequest(args))
189+
.put("data_dir", getterDataDir().absolutePath)
190+
.toString()
191+
}
192+
193+
private fun applyFdroidAutogenRequest(call: MethodCall): String {
194+
val args = call.arguments as? Map<*, *> ?: emptyMap<Any?, Any?>()
195+
return JSONObject(GetterBridgeRequestBuilder.autogenApplyRequest(args))
183196
.put("data_dir", getterDataDir().absolutePath)
184-
.put("preview", JSONObject(previewJson))
185-
.put(
186-
"acceptance",
187-
JSONObject()
188-
.put("mode", acceptance?.get("mode") as? String ?: "all")
189-
.put("package_ids", JSONArray(packageIds)),
190-
)
191197
.toString()
192198
}
193199

app_flutter/android/app/src/test/kotlin/net/xzos/upgradeall/GetterBridgeRequestBuilderTest.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,59 @@ class GetterBridgeRequestBuilderTest {
5151
assertEquals(0, json.getJSONObject("payload").length())
5252
}
5353

54+
@Test
55+
fun fdroidAutogenPreviewRequestPreservesGetterPayload() {
56+
val json = JSONObject(
57+
GetterBridgeRequestBuilder.fdroidAutogenPreviewRequest(
58+
mapOf(
59+
"payload" to mapOf(
60+
"index_xml" to "<fdroid />",
61+
"package_names" to listOf("org.fdroid.fdroid"),
62+
),
63+
),
64+
),
65+
)
66+
67+
val payload = json.getJSONObject("payload")
68+
assertEquals("<fdroid />", payload.getString("index_xml"))
69+
assertEquals("org.fdroid.fdroid", payload.getJSONArray("package_names").getString(0))
70+
}
71+
72+
@Test
73+
fun autogenApplyRequestPreservesPreviewAndAcceptance() {
74+
val json = JSONObject(
75+
GetterBridgeRequestBuilder.autogenApplyRequest(
76+
mapOf(
77+
"preview_json" to "{\"operation\":\"fdroid.autogen.preview\"}",
78+
"acceptance" to mapOf(
79+
"mode" to "packages",
80+
"package_ids" to listOf("android/f-droid/app/org.fdroid.fdroid"),
81+
),
82+
),
83+
),
84+
)
85+
86+
assertEquals(
87+
"fdroid.autogen.preview",
88+
json.getJSONObject("preview").getString("operation"),
89+
)
90+
val acceptance = json.getJSONObject("acceptance")
91+
assertEquals("packages", acceptance.getString("mode"))
92+
assertEquals(
93+
"android/f-droid/app/org.fdroid.fdroid",
94+
acceptance.getJSONArray("package_ids").getString(0),
95+
)
96+
}
97+
98+
@Test
99+
fun autogenApplyRequestRequiresPreviewJson() {
100+
val error = assertThrows(IllegalArgumentException::class.java) {
101+
GetterBridgeRequestBuilder.autogenApplyRequest(mapOf("acceptance" to emptyMap<String, String>()))
102+
}
103+
104+
assertEquals("preview_json is required", error.message)
105+
}
106+
54107
@Test
55108
fun runtimeOperationRequestRequiresOperation() {
56109
val error = assertThrows(IllegalArgumentException::class.java) {

app_flutter/android/getter_bridge/src/main/kotlin/net/xzos/upgradeall/getter/NativeLib.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class NativeLib {
1313
external fun initializeBridge(context: Context): String
1414
external fun previewInstalledAutogen(context: Context, requestJson: String): String
1515
external fun applyInstalledAutogen(requestJson: String): String
16+
external fun previewFdroidAutogen(requestJson: String): String
17+
external fun applyFdroidAutogen(requestJson: String): String
1618
external fun importLegacyRoomDatabase(requestJson: String): String
1719
external fun legacyReportList(requestJson: String): String
1820
external fun readOperation(requestJson: String): String

app_flutter/lib/cli_getter_adapter.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,31 @@ class CliGetterAdapter implements GetterAdapter {
9494
);
9595
}
9696

97+
@override
98+
Future<InstalledAutogenPreview> previewFdroidAutogen(
99+
Map<String, Object?> payload,
100+
) async {
101+
throw const GetterBridgeException(
102+
GetterError(
103+
code: 'bridge.unsupported',
104+
message: 'CLI adapter does not expose fixture-backed F-Droid autogen',
105+
),
106+
);
107+
}
108+
109+
@override
110+
Future<InstalledAutogenApplyResult> applyFdroidAutogen(
111+
InstalledAutogenPreview preview, {
112+
List<String>? acceptedPackageIds,
113+
}) async {
114+
throw const GetterBridgeException(
115+
GetterError(
116+
code: 'bridge.unsupported',
117+
message: 'CLI adapter does not expose fixture-backed F-Droid autogen',
118+
),
119+
);
120+
}
121+
97122
@override
98123
Future<RuntimeUpdateCheckResult> checkPackageForUpdate(
99124
String packageId, {

app_flutter/lib/getter_adapter.dart

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ abstract interface class GetterAdapter {
3131
List<String>? acceptedPackageIds,
3232
});
3333

34+
Future<InstalledAutogenPreview> previewFdroidAutogen(
35+
Map<String, Object?> payload,
36+
);
37+
38+
Future<InstalledAutogenApplyResult> applyFdroidAutogen(
39+
InstalledAutogenPreview preview, {
40+
List<String>? acceptedPackageIds,
41+
});
42+
3443
Future<RuntimeUpdateCheckResult> checkPackageForUpdate(
3544
String packageId, {
3645
String? repositoryId,
@@ -224,6 +233,61 @@ class FakeGetterAdapter implements GetterAdapter {
224233
});
225234
}
226235

236+
@override
237+
Future<InstalledAutogenPreview> previewFdroidAutogen(
238+
Map<String, Object?> payload,
239+
) async {
240+
return InstalledAutogenPreview.fromJson(const <String, Object?>{
241+
'operation': 'fdroid.autogen.preview',
242+
'provider': 'fdroid',
243+
'endpoint_id': 'official',
244+
'endpoint_url': 'https://f-droid.org/repo',
245+
'target_repo_id': 'autogen',
246+
'target_repo_path': '/fake/getter/repo/autogen',
247+
'summary': <String, Object?>{
248+
'candidate_count': 1,
249+
'skipped_count': 0,
250+
'write_count': 1,
251+
'delete_count': 0,
252+
},
253+
'candidates': <Object?>[
254+
<String, Object?>{
255+
'package_id': 'android/f-droid/app/org.fdroid.fdroid',
256+
'kind': 'android',
257+
'display_name': 'F-Droid',
258+
'installed_target': <String, Object?>{
259+
'kind': 'android_package',
260+
'package_name': 'org.fdroid.fdroid',
261+
},
262+
'action': 'create',
263+
'output_relative_path': 'android/f-droid/app/org.fdroid.fdroid',
264+
'content_hash': 'sha512:fake-fdroid',
265+
'content': '-- fake generated F-Droid content',
266+
},
267+
],
268+
'skipped': <Object?>[],
269+
'diagnostics': <Object?>[],
270+
});
271+
}
272+
273+
@override
274+
Future<InstalledAutogenApplyResult> applyFdroidAutogen(
275+
InstalledAutogenPreview preview, {
276+
List<String>? acceptedPackageIds,
277+
}) async {
278+
return InstalledAutogenApplyResult.fromJson(const <String, Object?>{
279+
'target_repo_id': 'autogen',
280+
'target_repo_path': '/fake/getter/repo/autogen',
281+
'applied_count': 1,
282+
'applied': <Object?>[
283+
<String, Object?>{
284+
'package_id': 'android/f-droid/app/org.fdroid.fdroid',
285+
'output_relative_path': 'android/f-droid/app/org.fdroid.fdroid',
286+
},
287+
],
288+
});
289+
}
290+
227291
@override
228292
Future<RuntimeUpdateCheckResult> checkPackageForUpdate(
229293
String packageId, {

app_flutter/lib/native_getter_adapter.dart

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,30 @@ class MethodChannelGetterAdapter extends FakeGetterAdapter {
8181
}) async {
8282
final data = await _invokeGetterData(
8383
'applyInstalledAutogen',
84-
<String, Object?>{
85-
'preview_json': jsonEncode(preview.rawJson),
86-
'acceptance': acceptedPackageIds == null
87-
? const <String, Object?>{'mode': 'all'}
88-
: <String, Object?>{
89-
'mode': 'packages',
90-
'package_ids': acceptedPackageIds,
91-
},
92-
},
84+
_autogenApplyArguments(preview, acceptedPackageIds),
85+
);
86+
return InstalledAutogenApplyResult.fromJson(data);
87+
}
88+
89+
@override
90+
Future<InstalledAutogenPreview> previewFdroidAutogen(
91+
Map<String, Object?> payload,
92+
) async {
93+
final data = await _invokeGetterData(
94+
'previewFdroidAutogen',
95+
<String, Object?>{'payload': payload},
96+
);
97+
return InstalledAutogenPreview.fromJson(data);
98+
}
99+
100+
@override
101+
Future<InstalledAutogenApplyResult> applyFdroidAutogen(
102+
InstalledAutogenPreview preview, {
103+
List<String>? acceptedPackageIds,
104+
}) async {
105+
final data = await _invokeGetterData(
106+
'applyFdroidAutogen',
107+
_autogenApplyArguments(preview, acceptedPackageIds),
93108
);
94109
return InstalledAutogenApplyResult.fromJson(data);
95110
}
@@ -314,6 +329,21 @@ class MethodChannelGetterAdapter extends FakeGetterAdapter {
314329
return <String, Object?>{'task_id': taskId};
315330
}
316331

332+
Map<String, Object?> _autogenApplyArguments(
333+
InstalledAutogenPreview preview,
334+
List<String>? acceptedPackageIds,
335+
) {
336+
return <String, Object?>{
337+
'preview_json': jsonEncode(preview.rawJson),
338+
'acceptance': acceptedPackageIds == null
339+
? const <String, Object?>{'mode': 'all'}
340+
: <String, Object?>{
341+
'mode': 'packages',
342+
'package_ids': acceptedPackageIds,
343+
},
344+
};
345+
}
346+
317347
Future<Map<String, Object?>> _invokeGetterData(
318348
String method,
319349
Map<String, Object?> arguments,

0 commit comments

Comments
 (0)