diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index c06ead8..24517f7 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -2,9 +2,9 @@ - + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 639c779..3692ce4 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -11,6 +11,7 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 74dd639..b2c751a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d7e0228..7b4bc73 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -37,6 +37,7 @@ android { dependencies { + implementation (libs.play.services.wearable) implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) @@ -47,5 +48,5 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) - implementation("de.javagl:obj:0.2.1") -} \ No newline at end of file + implementation("de.javagl:obj:0.4.0") //old: implementation("de.javagl:obj:0.2.1") +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c719bc4..4ebe9d0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,7 +19,10 @@ package="com.example.eyeSeeYou"> + + + + + diff --git a/build.gradle.kts b/build.gradle.kts index 922f551..e43a9e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,18 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.compose) apply false +} +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath (libs.gradle) // Usa la versione appropriata + classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22") // Usa la versione appropriata + // NOTA: Il plugin 'com.google.gms.google-services' NON è strettamente + // necessario solo per play-services-wearable, ma spesso è presente per altri servizi Firebase/GMS. + // classpath 'com.google.gms:google-services:4.4.1' // Aggiungi se usi altri servizi GMS/Firebase + } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d579c8f..8fa13ac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,8 +11,23 @@ activity = "1.10.1" constraintlayout = "2.2.1" arcore = "1.0.0-alpha03" core = "1.48.0" +playServicesWearable = "18.0.0" +composeBom = "2024.09.00" +composeMaterial = "1.2.1" +composeFoundation = "1.2.1" +wearToolingPreview = "1.0.0" +activityCompose = "1.8.0" +coreSplashscreen = "1.0.1" +gradle = "8.2.2" # O la versione specifica che ti serve per il classpath +legacySupportV4 = "1.0.0" +percentlayout = "1.0.0" +wear = "1.3.0" [libraries] +gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" } +androidx-legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "legacySupportV4" } +androidx-percentlayout = { module = "androidx.percentlayout:percentlayout", version.ref = "percentlayout" } +androidx-wear = { module = "androidx.wear:wear", version.ref = "wear" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } @@ -23,8 +38,22 @@ androidx-activity = { group = "androidx.activity", name = "activity", version.re androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-arcore = { group = "androidx.xr.arcore", name = "arcore", version.ref = "arcore" } core = { group = "com.google.ar", name = "core", version.ref = "core" } +play-services-wearable = { group = "com.google.android.gms", name = "play-services-wearable", version.ref = "playServicesWearable" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } +androidx-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-compose-material = { group = "androidx.wear.compose", name = "compose-material", version.ref = "composeMaterial" } +androidx-compose-foundation = { group = "androidx.wear.compose", name = "compose-foundation", version.ref = "composeFoundation" } +androidx-wear-tooling-preview = { group = "androidx.wear", name = "wear-tooling-preview", version.ref = "wearToolingPreview" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 6c25e4f..c890c02 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,4 +21,4 @@ dependencyResolutionManagement { rootProject.name = "EyeSeeYou" include(":app") - \ No newline at end of file +include(":wear") diff --git a/wear/.gitignore b/wear/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/wear/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts new file mode 100644 index 0000000..5e09d17 --- /dev/null +++ b/wear/build.gradle.kts @@ -0,0 +1,71 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + +android { + namespace = "com.example.eyeseeyou" + compileSdk = 35 + + defaultConfig { + applicationId = "com.example.eyeSeeYou" + minSdk = 28 + targetSdk = 35 + versionCode = 1 + versionName = "1.0" + + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + viewBinding = true + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.play.services.wearable) + implementation(libs.androidx.percentlayout) + implementation(libs.androidx.legacy.support.v4) + implementation("androidx.recyclerview:recyclerview:1.4.0") + implementation("androidx.preference:preference:1.2.1") + + // Librerie specifiche per Wear OS UI (se necessario per UI più complesse) + implementation(libs.androidx.wear) + + // Dipendenza necessaria per VibratorManager (API 31+) + implementation(libs.androidx.appcompat) // Potrebbe essere già presente + + implementation(libs.play.services.wearable) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.compose.material) + implementation(libs.androidx.compose.foundation) + implementation(libs.androidx.wear.tooling.preview) + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.core.splashscreen) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} \ No newline at end of file diff --git a/wear/lint.xml b/wear/lint.xml new file mode 100644 index 0000000..44fac75 --- /dev/null +++ b/wear/lint.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/wear/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml new file mode 100644 index 0000000..bbc44ec --- /dev/null +++ b/wear/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wear/src/main/java/com/example/eyeseeyou/presentation/MainActivity.kt b/wear/src/main/java/com/example/eyeseeyou/presentation/MainActivity.kt new file mode 100644 index 0000000..4ca09d4 --- /dev/null +++ b/wear/src/main/java/com/example/eyeseeyou/presentation/MainActivity.kt @@ -0,0 +1,65 @@ +package com.example.eyeseeyou.presentation + +import android.os.Bundle +import android.os.Build +import android.os.VibrationEffect +import android.os.Vibrator +import android.util.Log +import androidx.activity.ComponentActivity +import com.google.android.gms.wearable.MessageClient +import com.google.android.gms.wearable.MessageEvent +import com.google.android.gms.wearable.Wearable + +class MainActivity : ComponentActivity(), MessageClient.OnMessageReceivedListener { + + private var lastVibrationTime: Long = 0 + private var lastMessage: String? = null + private val MIN_DELAY_MS = 3500L + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Wearable.getMessageClient(this).addListener(this) + Log.d("WEAR", "Wearable message listener started") + } + + override fun onMessageReceived(event: MessageEvent) { + val message = String(event.data) + val now = System.currentTimeMillis() + val vibrator = getSystemService(Vibrator::class.java) + + Log.d("WEAR", "Message received: $message") + + if (vibrator?.hasVibrator() != true) { + Log.w("WEAR", "Device doesn't support vibration.") + return + } + + val isNewMessage = message != lastMessage + val isDelayRespected = now - lastVibrationTime >= MIN_DELAY_MS + + if (isNewMessage || isDelayRespected) { + vibrateOnce(vibrator) + lastMessage = message + lastVibrationTime = now + Log.d("WEAR", "Vibration performed for: $message") + } else { + Log.d("WEAR", "Message ignored: $message") + } + } + + private fun vibrateOnce(vibrator: Vibrator) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val effect = VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE) + vibrator.vibrate(effect) + } else { + @Suppress("DEPRECATION") + vibrator.vibrate(500) + } + } + + override fun onDestroy() { + super.onDestroy() + Wearable.getMessageClient(this).removeListener(this) + Log.d("WEAR", "Wearable message listener removed") + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/eyeseeyou/presentation/MessageListenerService.kt b/wear/src/main/java/com/example/eyeseeyou/presentation/MessageListenerService.kt new file mode 100644 index 0000000..fd38219 --- /dev/null +++ b/wear/src/main/java/com/example/eyeseeyou/presentation/MessageListenerService.kt @@ -0,0 +1,107 @@ +package com.example.eyeseeyou.presentation + +import android.Manifest +import android.os.Build +import android.os.VibrationEffect +import android.os.Vibrator +import android.os.VibratorManager +import android.util.Log +import androidx.annotation.RequiresPermission +import com.example.eyeseeyou.presentation.util.WatchPreferences +import com.google.android.gms.wearable.MessageEvent +import com.google.android.gms.wearable.WearableListenerService + +class MessageListenerService : WearableListenerService() { + + companion object { + private const val TAG = "WearMessageListener" + } + + @RequiresPermission(Manifest.permission.VIBRATE) + override fun onMessageReceived(messageEvent: MessageEvent) { + val command = String(messageEvent.data) + + if (messageEvent.path == "/vibration_command") { + Log.d(TAG, "Received vibration command: $command") + + when (command) { + "sx" -> { + if (isVibrationEnabled()) { + Log.d(TAG, "Left Vibration.") + triggerVibration(longArrayOf(0, 100, 150, 500)) + } + } + + "dx" -> { + if (isVibrationEnabled()) { + Log.d(TAG, "Right vibration.") + triggerVibration(longArrayOf(0, 500, 150, 100)) + } + } + + "up" -> { + if (isVibrationEnabled()) { + Log.d(TAG, "Center vibration.") + triggerVibration(longArrayOf(0, 300)) + } + } + + "down" -> { + if (isVibrationEnabled()) { + Log.d(TAG, "Low vibration.") + triggerVibration(longArrayOf(0, 300, 150, 300)) + } + } + + "dangerous" -> { + if (isVibrationEnabled()) { + Log.d(TAG, "Stop vibration") + triggerVibration(longArrayOf(0, 1000)) + } + } + + "generics" -> { + if (isVibrationEnabled()) { + Log.d(TAG, "Generic vibration.") + triggerVibration(longArrayOf(0, 200)) + } + } + else -> { + Log.w(TAG, "Wrong command: $command") + } + } + } else { + super.onMessageReceived(messageEvent) + } + } + + @RequiresPermission(Manifest.permission.VIBRATE) + private fun triggerVibration(pattern: LongArray) { + val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val vibratorManager = getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager + vibratorManager.defaultVibrator + } else { + @Suppress("DEPRECATION") + getSystemService(VIBRATOR_SERVICE) as Vibrator + } + + if (!vibrator.hasVibrator()) { + Log.w(TAG, "Not supported vibration.") + return + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val vibrationEffect = VibrationEffect.createWaveform(pattern, -1) + vibrator.vibrate(vibrationEffect) + Log.d(TAG, "Vibration started waveform (API >= 26): ${pattern.joinToString()}") + } else { + @Suppress("DEPRECATION") + vibrator.vibrate(pattern, -1) + Log.d(TAG, "Vibration started waveform legacy (API < 26): ${pattern.joinToString()}") + } + } + + private fun isVibrationEnabled(): Boolean { + return WatchPreferences.isVibrationEnabled(applicationContext) + } +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/eyeseeyou/presentation/theme/Theme.kt b/wear/src/main/java/com/example/eyeseeyou/presentation/theme/Theme.kt new file mode 100644 index 0000000..28bf2cf --- /dev/null +++ b/wear/src/main/java/com/example/eyeseeyou/presentation/theme/Theme.kt @@ -0,0 +1,17 @@ +package com.example.eyeseeyou.presentation.theme + +import androidx.compose.runtime.Composable +import androidx.wear.compose.material.MaterialTheme + +@Composable +fun EyeSeeYouTheme( + content: @Composable () -> Unit +) { + /** + * Empty theme to customize for your app. + * See: https://developer.android.com/jetpack/compose/designsystems/custom + */ + MaterialTheme( + content = content + ) +} \ No newline at end of file diff --git a/wear/src/main/java/com/example/eyeseeyou/presentation/util/WatchPreferences.kt b/wear/src/main/java/com/example/eyeseeyou/presentation/util/WatchPreferences.kt new file mode 100644 index 0000000..c303502 --- /dev/null +++ b/wear/src/main/java/com/example/eyeseeyou/presentation/util/WatchPreferences.kt @@ -0,0 +1,19 @@ +package com.example.eyeseeyou.presentation.util + +import android.content.Context +import androidx.preference.PreferenceManager + +object WatchPreferences { + + private const val KEY_VIBRATION_ENABLED = "key_vibration_enabled" + + fun isVibrationEnabled(context: Context): Boolean { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + return prefs.getBoolean(KEY_VIBRATION_ENABLED, true) // true = default attivo + } + + fun setVibrationEnabled(context: Context, enabled: Boolean) { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + prefs.edit().putBoolean(KEY_VIBRATION_ENABLED, enabled).apply() + } +} \ No newline at end of file diff --git a/wear/src/main/res/drawable/ic_launcher_background.xml b/wear/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/wear/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wear/src/main/res/drawable/ic_launcher_foreground.xml b/wear/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/wear/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/wear/src/main/res/drawable/splash_icon.xml b/wear/src/main/res/drawable/splash_icon.xml new file mode 100644 index 0000000..7874e83 --- /dev/null +++ b/wear/src/main/res/drawable/splash_icon.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/wear/src/main/res/layout/activity_main.xml b/wear/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..994843e --- /dev/null +++ b/wear/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/wear/src/main/res/mipmap-anydpi/ic_launcher.xml b/wear/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/wear/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/wear/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/wear/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/wear/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher.webp b/wear/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/wear/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/wear/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/wear/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/wear/src/main/res/mipmap-mdpi/ic_launcher.webp b/wear/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/wear/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/wear/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/wear/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/wear/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/wear/src/main/res/mipmap-xhdpi/ic_launcher.webp b/wear/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/wear/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/wear/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/wear/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/wear/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/wear/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/wear/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/wear/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/wear/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/wear/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/wear/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/wear/src/main/res/values-round/strings.xml b/wear/src/main/res/values-round/strings.xml new file mode 100644 index 0000000..42f1229 --- /dev/null +++ b/wear/src/main/res/values-round/strings.xml @@ -0,0 +1,3 @@ + + From the Round world,\nHello, %1$s! + \ No newline at end of file diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml new file mode 100644 index 0000000..151c5d2 --- /dev/null +++ b/wear/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + EyeSeeYou + + From the Square world,\nHello, %1$s! + \ No newline at end of file diff --git a/wear/src/main/res/values/styles.xml b/wear/src/main/res/values/styles.xml new file mode 100644 index 0000000..dbf9c83 --- /dev/null +++ b/wear/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file