Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/AndroidProjectSystem.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions .idea/deviceManager.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/migrations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/runConfigurations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 15 additions & 29 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'org.jetbrains.kotlin.android'
id 'com.google.gms.google-services'
}

Expand All @@ -16,47 +16,38 @@ android {
}

buildTypes {
debug {
debuggable true
}
debug { debuggable true }
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
//minifyEnabled true

// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
//shrinkResources true

// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug

//debuggable true
}
}

// AGP 8.x expects JDK 17 toolchain
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}

namespace 'com.meshcentral.agent'
}

dependencies {
//noinspection GradleDependency
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
// Kotlin stdlib (pin explicitly since we removed ext.kotlin_version)
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.10"

implementation 'androidx.core:core-ktx:1.12.0'
//implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
implementation 'com.budiyev.android:code-scanner:2.1.0'

// Replaced old JCenter artifact with Maven Central one
implementation "io.github.yuriy-budiyev:code-scanner:2.3.2"

implementation 'com.karumi:dexter:6.2.2'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
Expand All @@ -68,9 +59,4 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'com.google.firebase:firebase-installations-ktx:17.2.0'
//implementation 'androidx.work:work-runtime-ktx:2.9.0'
//implementation 'org.webrtc:google-webrtc:1.0.32006'
//testImplementation 'junit:junit:4.13.1'
//androidTestImplementation 'androidx.test.ext:junit:1.1.2'
//androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
11 changes: 11 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>


<application
android:allowBackup="true"
Expand Down Expand Up @@ -63,5 +65,14 @@
<service
android:name=".ScreenCaptureService"
android:foregroundServiceType="mediaProjection" />

<service
android:name=".annotation.AnnotationOverlayService"
android:exported="false"
android:foregroundServiceType="mediaProjection" />

<receiver
android:name=".annotation.StopAnnotationReceiver"
android:exported="false" />
</application>
</manifest>
25 changes: 22 additions & 3 deletions app/src/main/java/com/meshcentral/agent/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import com.google.firebase.messaging.FirebaseMessaging
import com.meshcentral.agent.annotation.AnnotationConsent
import com.meshcentral.agent.annotation.AnnotationController
import com.meshcentral.agent.annotation.AnnotationFeature
import com.meshcentral.agent.annotation.AnnotationFeature.stopAnnotations
import com.meshcentral.agent.annotation.AnnotationPrefs
import com.meshcentral.agent.annotation.AnnotationServiceBus
import org.json.JSONObject
import org.spongycastle.asn1.x500.X500Name
import org.spongycastle.cert.X509v3CertificateBuilder
Expand Down Expand Up @@ -81,6 +87,8 @@ var pendingActivities : ArrayList<PendingActivityData> = ArrayList<PendingActivi
var pushMessagingToken : String? = null
var g_autoConnect : Boolean = true
var g_autoConsent : Boolean = false
var g_autoAnnotation: Boolean = false
var g_autoConsentNotification : Boolean = true
var g_userDisconnect : Boolean = false // Indicate user initiated disconnection
var g_retryTimer: CountDownTimer? = null

Expand Down Expand Up @@ -224,6 +232,8 @@ class MainActivity : AppCompatActivity() {
} else {
item9.isVisible = false
}
var item10 = menu.findItem(R.id.action_stopannotations);
item10.isVisible = AnnotationServiceBus.isActive()
return true
}

Expand Down Expand Up @@ -257,6 +267,11 @@ class MainActivity : AppCompatActivity() {
stopProjection()
}

if (item.itemId == R.id.action_stopannotations) {
// Stop annotations
stopAnnotations(this)
}

if ((item.itemId == R.id.action_manual_setup_server) && (hardCodedServerLink == null)) {
// Manually setup the server pairing
promptForServerLink()
Expand Down Expand Up @@ -297,10 +312,12 @@ class MainActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
println("onActivityResult, requestCode: $requestCode, resultCode: $resultCode, data: ${data.toString()}")
super.onActivityResult(requestCode, resultCode, data)
AnnotationController.onActivityResult(this, requestCode)

if (requestCode == MainActivity.Companion.REQUEST_CODE) {
if (resultCode == RESULT_OK) {
startService(com.meshcentral.agent.ScreenCaptureService.getStartIntent(this, resultCode, data))
AnnotationFeature.onScreenShareStarted(this)
if (meshAgent?.tunnels?.getOrNull(0) != null) {
val json = JSONObject()
json.put("type", "console")
Expand Down Expand Up @@ -674,6 +691,7 @@ class MainActivity : AppCompatActivity() {
// Stop screen sharing
fun stopProjection() {
if (g_ScreenCaptureService == null) return
AnnotationFeature.onScreenShareStopped(this)
startService(com.meshcentral.agent.ScreenCaptureService.getStopIntent(this))
}

Expand All @@ -682,6 +700,9 @@ class MainActivity : AppCompatActivity() {
val pm: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
g_autoConnect = pm.getBoolean("pref_autoconnect", false)
g_autoConsent = pm.getBoolean("pref_autoconsent", false)
g_autoAnnotation = pm.getBoolean("pref_annotation_auto", false)
g_autoConsentNotification = pm.getBoolean("pref_autoconsentnotifcation", true)
AnnotationPrefs.setAutoEnabled(this, g_autoAnnotation)
g_userDisconnect = false
if (g_autoConnect == false) {
if (g_retryTimer != null) {
Expand All @@ -693,10 +714,8 @@ class MainActivity : AppCompatActivity() {
toggleAgentConnection(false)
}
}
if (g_autoConsent) {
if (g_autoConsent && g_ScreenCaptureService?.isProjectionActive() == false) {
startProjection()
} else if (!g_autoConsent && g_ScreenCaptureService != null) {
stopProjection()
}
}
}
Expand Down
41 changes: 40 additions & 1 deletion app/src/main/java/com/meshcentral/agent/MeshAgent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import android.net.Uri
import android.os.*
import android.provider.Settings
import android.util.Base64
import com.meshcentral.agent.annotation.AnnotationBridge
import com.meshcentral.agent.annotation.AnnotationConsent
import okhttp3.*
import okio.ByteString
import okio.ByteString.Companion.toByteString
Expand Down Expand Up @@ -305,6 +307,7 @@ class MeshAgent(parent: MainActivity, host: String, certHash: String, devGroupId
UpdateState(3) // Switch to connected and verified
startConnectionTimer()
sendCoreInfo()
sendAnnotationCaps()
sendNetworkUpdate(false)
sendServerImageRequest()

Expand Down Expand Up @@ -406,7 +409,7 @@ class MeshAgent(parent: MainActivity, host: String, certHash: String, devGroupId
"console" -> {
processConsoleMessage(json.getString("value"), json.getString("sessionid"), json)
}
"tunnel" -> {
"tunnel" -> {
/*
{"action":"msg",
"type":"tunnel",
Expand Down Expand Up @@ -511,6 +514,23 @@ class MeshAgent(parent: MainActivity, host: String, certHash: String, devGroupId
parent.refreshInfo()
}
}
"annotation" -> {
val op = json.optString("op", "")

// Allow probe (and start/style/remove if you want) without ScreenCaptureService.
val opsAllowedWithoutCapture = setOf("probe", "start", "style", "remove")

if (!opsAllowedWithoutCapture.contains(op)) {
if (g_ScreenCaptureService == null) return
}

if (op == "start") {
AnnotationConsent.requestEnableWithConsent(parent)
}

val ack = AnnotationBridge.handleFromServer(parent, json)
ack?.let { _webSocket?.send(it.toString().toByteArray().toByteString()) }
}
else -> {
// Unknown command, ignore it.
println("Unhandled action: $action")
Expand All @@ -532,6 +552,21 @@ class MeshAgent(parent: MainActivity, host: String, certHash: String, devGroupId
if (_webSocket != null) { _webSocket?.send(r.toString().toByteArray().toByteString()) }
}

// Send Annotation Capability
fun sendAnnotationCaps() {
try {
val hasPerm = if (Build.VERSION.SDK_INT >= 23)
Settings.canDrawOverlays(parent)
else true

val r = JSONObject()
r.put("action", "annotationcaps")
r.put("annotation", true) // feature supported
r.put("annotationPermission", if (hasPerm) "granted" else "denied")
_webSocket?.send(r.toString().toByteArray().toByteString())
} catch (_: Exception) { /* ignore */ }
}

// Send 2FA authentication URL and approval/reject back
fun send2faAuth(url: Uri, approved: Boolean) {
val r = JSONObject()
Expand Down Expand Up @@ -1033,4 +1068,8 @@ class MeshAgent(parent: MainActivity, host: String, certHash: String, devGroupId
if (_webSocket != null) { _webSocket?.send(json.toString().toByteArray().toByteString()) }
}

fun sendJson(obj: JSONObject) {
_webSocket?.send(obj.toString().toByteArray().toByteString())
}

}
Loading