Skip to content

Commit 38bfbd3

Browse files
feat. added native layer
1 parent f1c021f commit 38bfbd3

File tree

9 files changed

+293
-17
lines changed

9 files changed

+293
-17
lines changed

android/app/build.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,7 @@ dependencies {
6363
implementation fileTree(include: ['*.jar'], dir: 'libs')
6464
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
6565
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
66-
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
6766
implementation project(':capacitor-android')
68-
testImplementation "junit:junit:$junitVersion"
69-
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
70-
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
7167
implementation project(':capacitor-cordova-android-plugins')
7268
}
7369

android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<uses-permission android:name="android.permission.INTERNET" />
77

88
<application
9+
android:name=".Acode"
910
android:allowBackup="true"
1011
android:icon="@mipmap/ic_launcher"
1112
android:label="@string/app_name"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.foxdebug.acode
2+
3+
import android.app.Application
4+
5+
class Acode : Application() {
6+
companion object{
7+
lateinit var instance: Acode
8+
private set
9+
}
10+
override fun onCreate() {
11+
super.onCreate()
12+
instance = this
13+
}
14+
}
Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,47 @@
11
package com.foxdebug.acode
22

3+
import android.content.Context
4+
import android.os.Bundle
5+
import androidx.lifecycle.lifecycleScope
6+
import com.foxdebug.acode.plugins.NativeLayer
7+
import com.foxdebug.acode.plugins.Sftp
38
import com.getcapacitor.BridgeActivity
9+
import kotlinx.coroutines.CoroutineScope
10+
import java.lang.ref.WeakReference
411

5-
class MainActivity : BridgeActivity()
12+
13+
class MainActivity : BridgeActivity(){
14+
15+
companion object{
16+
private var activityRef: WeakReference<MainActivity?>? = WeakReference(null)
17+
fun getActivityContext(): Context?{
18+
return activityRef?.get()
19+
}
20+
21+
val lifeCycleScope: CoroutineScope get() {
22+
val scope = activityRef?.get()?.lifecycleScope
23+
return if (scope == null){
24+
throw IllegalStateException("Activity is not available")
25+
}else{
26+
scope
27+
}
28+
}
29+
30+
}
31+
32+
override fun onCreate(savedInstanceState: Bundle?) {
33+
super.onCreate(savedInstanceState)
34+
activityRef = WeakReference(this)
35+
36+
//register plugins
37+
registerPlugin(Sftp::class.java)
38+
registerPlugin(NativeLayer::class.java)
39+
40+
}
41+
42+
override fun onDestroy() {
43+
super.onDestroy()
44+
activityRef = WeakReference(null)
45+
activityRef = null
46+
}
47+
}

android/app/src/main/java/com/foxdebug/acode/Sftp.kt

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.foxdebug.acode
2+
3+
import kotlinx.coroutines.CoroutineScope
4+
import kotlinx.coroutines.DelicateCoroutinesApi
5+
import kotlinx.coroutines.Dispatchers
6+
import kotlinx.coroutines.GlobalScope
7+
import kotlinx.coroutines.launch
8+
9+
object Utils {
10+
//guarantees the runnable to be run on the main thread
11+
@OptIn(DelicateCoroutinesApi::class)
12+
fun runOnUiThread(callback: CoroutineScope.() -> Unit) {
13+
GlobalScope.launch(Dispatchers.Main){
14+
callback.invoke(this)
15+
}
16+
}
17+
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package com.foxdebug.acode.plugins
2+
3+
import android.content.Intent
4+
import android.net.Uri
5+
import android.os.Build
6+
import androidx.appcompat.app.AlertDialog
7+
import com.foxdebug.acode.Acode
8+
import com.foxdebug.acode.MainActivity
9+
import com.foxdebug.acode.Utils
10+
import com.getcapacitor.JSObject
11+
import com.getcapacitor.Plugin
12+
import com.getcapacitor.PluginCall
13+
import com.getcapacitor.PluginMethod
14+
import com.getcapacitor.annotation.CapacitorPlugin
15+
16+
@CapacitorPlugin(name = "NativeLayer")
17+
class NativeLayer : Plugin() {
18+
19+
/**
20+
* Displays a dialog with the provided title and message.
21+
*
22+
* @param call The PluginCall containing the dialog parameters. It should contain:
23+
* - `title`: The title of the dialog (String).
24+
* - `message`: The message displayed in the dialog (String).
25+
*
26+
* If either `title` or `message` is missing, the call will be rejected with an appropriate error message.
27+
* Upon clicking "OK", the dialog will resolve the call.
28+
*/
29+
@PluginMethod
30+
fun showDialog(call: PluginCall) {
31+
if (call.has("title").not()) {
32+
call.reject("title not provided")
33+
return
34+
}
35+
if (call.has("message").not()) {
36+
call.reject("message not provided")
37+
return
38+
}
39+
40+
val title = call.getString("title")
41+
val message = call.getString("message")
42+
43+
Utils.runOnUiThread {
44+
val context = MainActivity.getActivityContext()
45+
if (context == null) {
46+
call.reject("Context is null")
47+
return@runOnUiThread
48+
}
49+
AlertDialog.Builder(context).apply {
50+
setTitle(title)
51+
setMessage(message)
52+
setPositiveButton("OK",null)
53+
show()
54+
}
55+
call.resolve()
56+
}
57+
58+
}
59+
60+
/**
61+
* Launches an intent based on the provided parameters.
62+
*
63+
* @param call The PluginCall containing the intent parameters. It should contain:
64+
* - `constructor_number`: The type of intent constructor to use (Int).
65+
* - `launch_type`: The type of intent launch ("activity" or "service").
66+
* - `activity_context`: Whether to use the activity context (Boolean).
67+
* - `action`: The action for the intent (String) (for constructor 0).
68+
* - `className`: The class name to launch (String) (for constructor 1).
69+
* - `new_task`: Whether to launch the intent in a new task (Boolean).
70+
* - `extras`: A JSObject containing key-value pairs to add as extras to the intent.
71+
* - `data`: Optional URI to set as the intent data.
72+
* - `type`: Optional MIME type for the intent.
73+
* - `package` and `class`: Optional package and class for explicit intents.
74+
*
75+
* This method supports multiple constructors based on the `constructor_number`:
76+
* - `0`: Uses an action string to create an intent.
77+
* - `1`: Uses a class name to create an explicit intent.
78+
* - `2`: Creates a generic intent.
79+
*
80+
* If the `launch_type` is "activity", it starts an activity.
81+
* If the `launch_type` is "service", it starts a service, and you can specify if it's a foreground service.
82+
*
83+
* If any required parameters are missing, the call will be rejected with an appropriate error message.
84+
*/
85+
@PluginMethod
86+
fun launchIntent(call: PluginCall) {
87+
val constructorNumber = call.getInt("constructor_number") ?: run {
88+
call.reject("Missing 'constructor_number'")
89+
return
90+
}
91+
92+
val launchType = call.getString("launch_type") ?: run {
93+
call.reject("Missing 'launch_type'")
94+
return
95+
}
96+
97+
val useActivityContext = call.getBoolean("activity_context") ?: run {
98+
call.reject("Missing 'activity_context'")
99+
return
100+
}
101+
102+
val context = if (useActivityContext) MainActivity.getActivityContext() else Acode.instance
103+
if (context == null) {
104+
call.reject("Requested context is not available")
105+
return
106+
}
107+
108+
val intent = when (constructorNumber) {
109+
0 -> {
110+
val action = call.getString("action") ?: run {
111+
call.reject("Missing 'action' for constructor 0")
112+
return
113+
}
114+
Intent(action)
115+
}
116+
117+
1 -> {
118+
val className = call.getString("className") ?: run {
119+
call.reject("Missing 'class' for constructor 1")
120+
return
121+
}
122+
Intent(context, Class.forName(className))
123+
}
124+
125+
2 -> {
126+
Intent()
127+
}
128+
129+
else -> {
130+
call.reject("Unsupported constructor_number: $constructorNumber")
131+
return
132+
}
133+
}
134+
135+
if (call.getBoolean("new_task") == true) {
136+
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
137+
}
138+
139+
call.getObject("extras", JSObject())?.let { extras ->
140+
for (key in extras.keys()) {
141+
when (val value = extras.get(key)) {
142+
is String -> intent.putExtra(key, value)
143+
is Int -> intent.putExtra(key, value)
144+
is Boolean -> intent.putExtra(key, value)
145+
is Double -> intent.putExtra(key, value)
146+
is Float -> intent.putExtra(key, value)
147+
else -> {
148+
call.reject("Unsupported data type encountered while processing intent extras")
149+
return
150+
}
151+
}
152+
}
153+
}
154+
155+
call.getString("data")?.let {
156+
intent.data = Uri.parse(it)
157+
}
158+
159+
call.getString("type")?.let {
160+
intent.type = it
161+
}
162+
163+
if (call.has("package") && call.has("class")) {
164+
val pkg = call.getString("package")
165+
val cls = call.getString("class")
166+
if (pkg != null && cls != null) {
167+
intent.setClassName(pkg, cls)
168+
}
169+
}
170+
171+
when (launchType) {
172+
"activity" -> {
173+
context.startActivity(intent)
174+
call.resolve()
175+
}
176+
177+
"service" -> {
178+
val isForeground = call.getBoolean("foreground_service") ?: run {
179+
call.reject("Missing 'foreground_service' for service launch")
180+
return
181+
}
182+
183+
if (isForeground) {
184+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
185+
context.startForegroundService(intent)
186+
call.resolve()
187+
} else {
188+
call.unavailable("Foreground service requires API 26+")
189+
}
190+
} else {
191+
context.startService(intent)
192+
call.resolve()
193+
}
194+
}
195+
196+
else -> {
197+
call.reject("Invalid launch_type: $launchType")
198+
}
199+
}
200+
}
201+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.foxdebug.acode.plugins
2+
3+
import com.getcapacitor.PluginCall
4+
5+
6+
fun PluginCall.has(key: String): Boolean {
7+
return data.has(key)
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.foxdebug.acode.plugins
2+
3+
import com.getcapacitor.Plugin
4+
import com.getcapacitor.annotation.CapacitorPlugin;
5+
6+
@CapacitorPlugin(name = "Sftp")
7+
class Sftp : Plugin() {
8+
9+
}

0 commit comments

Comments
 (0)