feat(android): renommage package com.gametime.app → com.idea.gametime

- Mise à jour namespace et applicationId dans build.gradle.kts
- Déplacement des fichiers MainActivity.kt, session/, watch/ vers com/idea/gametime/
- Ajout garde-fou usesCleartextTraffic uniquement en debug/profile
- Nettoyage .gitignore pour exclure .gradle/ au niveau racine

Préparation ticket #261 release Android closed test
This commit is contained in:
2026-08-28 23:06:36 +02:00
parent 9dbfd8d8ea
commit 2eda267346
14 changed files with 580 additions and 56 deletions

View File

@ -28,7 +28,7 @@ check(sharedSigningStoreFile.isFile) {
}
android {
namespace = "com.gametime.app"
namespace = "com.idea.gametime"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@ -38,8 +38,8 @@ android {
}
defaultConfig {
applicationId = "com.gametime.app"
minSdk = flutter.minSdkVersion
applicationId = "com.idea.gametime"
minSdk = 26
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
@ -72,5 +72,7 @@ flutter {
}
dependencies {
implementation("androidx.health.connect:connect-client:1.1.0")
implementation("com.google.android.gms:play-services-wearable:19.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
}

View File

@ -4,4 +4,6 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<application android:usesCleartextTraffic="true"/>
</manifest>

View File

@ -6,12 +6,15 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.health.WRITE_EXERCISE"/>
<uses-permission android:name="android.permission.health.WRITE_DISTANCE"/>
<uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED"/>
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE"/>
<application
android:label="GameTime"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.google.android.wearable.capabilities"
android:resource="@array/android_wear_capabilities" />
@ -37,6 +40,23 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".health.HealthConnectRationaleActivity"
android:exported="true">
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:targetActivity=".health.HealthConnectRationaleActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
@ -73,6 +93,10 @@
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<package android:name="com.google.android.apps.healthdata" />
<intent>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>

View File

@ -1,14 +0,0 @@
package com.gametime.app
import com.gametime.app.session.SessionNotificationPlugin
import com.gametime.app.watch.WatchBridgePlugin
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
WatchBridgePlugin.register(flutterEngine, applicationContext)
SessionNotificationPlugin.register(flutterEngine, this)
}
}

View File

@ -0,0 +1,22 @@
package com.idea.gametime
import android.os.Bundle
import com.idea.gametime.health.HealthConnectPlugin
import com.idea.gametime.session.SessionNotificationPlugin
import com.idea.gametime.watch.WatchBridgePlugin
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
HealthConnectPlugin.prepare(this)
super.onCreate(savedInstanceState)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
WatchBridgePlugin.register(flutterEngine, applicationContext)
SessionNotificationPlugin.register(flutterEngine, this)
HealthConnectPlugin.register(flutterEngine, this)
}
}

View File

@ -0,0 +1,470 @@
package com.idea.gametime.health
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.PermissionController
import androidx.lifecycle.Lifecycle
import androidx.health.connect.client.permission.HealthPermission
import androidx.health.connect.client.records.DistanceRecord
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.Record
import androidx.health.connect.client.records.TotalCaloriesBurnedRecord
import androidx.health.connect.client.records.metadata.Metadata
import androidx.health.connect.client.units.Energy
import androidx.health.connect.client.units.Length
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.lang.ref.WeakReference
import java.time.Instant
import java.time.ZoneOffset
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
object HealthConnectPlugin {
private const val METHOD_CHANNEL = "gametime.health_connect/methods"
private const val LOG_TAG = "GTHealthConnect"
private const val HEALTH_CONNECT_PROVIDER_PACKAGE = "com.google.android.apps.healthdata"
private const val PLAY_STORE_PACKAGE = "com.android.vending"
private const val FALLBACK_OPEN_DELAY_MS = 1200L
private val requiredPermissions =
setOf(
HealthPermission.getWritePermission(ExerciseSessionRecord::class),
HealthPermission.getWritePermission(DistanceRecord::class),
HealthPermission.getWritePermission(TotalCaloriesBurnedRecord::class),
HealthPermission.getWritePermission(HeartRateRecord::class),
)
private var appContext: Context? = null
private var activityRef: WeakReference<Activity>? = null
private var permissionRequestLauncher: ActivityResultLauncher<Set<String>>? = null
private var pendingPermissionResult: MethodChannel.Result? = null
private var fallbackOpenRunnable: Runnable? = null
private val mainHandler = Handler(Looper.getMainLooper())
private val mainScope = CoroutineScope(Dispatchers.Main)
fun prepare(activity: Activity) {
appContext = activity.applicationContext
activityRef = WeakReference(activity)
registerPermissionRequestLauncher(activity)
}
fun register(flutterEngine: FlutterEngine, activity: Activity) {
appContext = activity.applicationContext
activityRef = WeakReference(activity)
if (permissionRequestLauncher == null) {
registerPermissionRequestLauncher(activity)
}
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, METHOD_CHANNEL)
.setMethodCallHandler(::handleMethodCall)
}
private fun registerPermissionRequestLauncher(activity: Activity) {
Log.d(LOG_TAG, "registerPermissionRequestLauncher(activity=${activity::class.java.name})")
val componentActivity = activity as? ComponentActivity
if (componentActivity == null) {
Log.e(LOG_TAG, "Activity is not a ComponentActivity; permission launcher unavailable.")
return
}
try {
val context = activity.applicationContext
permissionRequestLauncher?.unregister()
permissionRequestLauncher =
componentActivity.activityResultRegistry.register(
"gametime_health_connect_permissions",
PermissionController.createRequestPermissionResultContract(),
) { grantedPermissions ->
Log.d(
LOG_TAG,
"permission request callback(granted=${grantedPermissions.size})",
)
cancelFallbackOpen()
val result = pendingPermissionResult ?: return@register
pendingPermissionResult = null
mainScope.launch {
val state = try {
connectionStateMap(
context,
grantedPermissions,
permissionRequestOutcome =
if (grantedPermissions.containsAll(requiredPermissions)) {
"granted"
} else {
"denied"
},
)
} catch (error: Throwable) {
Log.e(LOG_TAG, "connection state failed after callback.", error)
result.error("health_connect_state_failed", error.message, null)
return@launch
}
result.success(state)
}
}
Log.d(LOG_TAG, "permission launcher registered.")
} catch (error: Throwable) {
Log.e(LOG_TAG, "permission launcher registration failed.", error)
permissionRequestLauncher = null
}
}
private fun handleMethodCall(call: MethodCall, result: MethodChannel.Result) {
val context = appContext
if (context == null) {
result.error("health_connect_unavailable", "Application context unavailable.", null)
return
}
when (call.method) {
"connectionState" -> connectionState(context, result)
"requestPermissions" -> requestPermissions(context, result)
"openSettings" -> openSettings(context, result)
"exportWorkout" -> exportWorkout(context, call.arguments, result)
else -> result.notImplemented()
}
}
private fun connectionState(context: Context, result: MethodChannel.Result) {
mainScope.launch {
try {
result.success(connectionStateMap(context))
} catch (error: Throwable) {
result.error("health_connect_state_failed", error.message, null)
}
}
}
private suspend fun connectionStateMap(
context: Context,
grantedOverride: Set<String>? = null,
permissionRequestOutcome: String? = null,
): Map<String, Any> {
var availability = availability(context)
val grantedPermissions = if (availability == "available") {
try {
grantedOverride ?: HealthConnectClient.getOrCreate(context)
.permissionController
.getGrantedPermissions()
} catch (_: SecurityException) {
availability = "accessBlocked"
emptySet()
} catch (_: IllegalStateException) {
availability = "accessBlocked"
emptySet()
}
} else {
emptySet()
}
val state = mutableMapOf<String, Any>(
"availability" to availability,
"requiredPermissions" to requiredPermissions.toList(),
"grantedPermissions" to grantedPermissions.toList(),
)
if (permissionRequestOutcome != null) {
state["permissionRequestOutcome"] = permissionRequestOutcome
}
return state
}
private fun availability(context: Context): String {
return when (HealthConnectClient.getSdkStatus(context)) {
HealthConnectClient.SDK_AVAILABLE -> "available"
HealthConnectClient.SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED ->
"providerUpdateRequired"
else -> "unavailable"
}
}
private fun requestPermissions(context: Context, result: MethodChannel.Result) {
val availability = availability(context)
Log.d(LOG_TAG, "requestPermissions availability=$availability")
if (availability != "available") {
mainScope.launch {
result.success(connectionStateMap(context))
}
return
}
val activity = activityRef?.get()
if (activity == null) {
Log.e(LOG_TAG, "requestPermissions failed: activity unavailable.")
result.error("health_connect_unavailable", "Activity unavailable.", null)
return
}
if (pendingPermissionResult != null) {
Log.e(LOG_TAG, "requestPermissions failed: request already active.")
result.error(
"health_connect_request_in_progress",
"Permission request already active.",
null,
)
return
}
pendingPermissionResult = result
val launcher = permissionRequestLauncher
if (launcher == null) {
Log.e(LOG_TAG, "requestPermissions fallback: launcher unavailable.")
openSettingsFallback(context, activity, "settingsFallback")
return
}
try {
Log.d(LOG_TAG, "launching Health Connect permission contract.")
launcher.launch(requiredPermissions)
Log.d(LOG_TAG, "Health Connect permission contract launch returned.")
scheduleFallbackOpen(context, activity)
} catch (error: Throwable) {
Log.e(LOG_TAG, "Health Connect permission contract launch failed.", error)
openSettingsFallback(context, activity, "launchFailed")
}
}
private fun scheduleFallbackOpen(context: Context, activity: Activity) {
cancelFallbackOpen()
val runnable = Runnable {
if (pendingPermissionResult == null) {
return@Runnable
}
val componentActivity = activity as? ComponentActivity
if (componentActivity != null &&
!componentActivity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
) {
Log.d(LOG_TAG, "permission UI appears to have focus; fallback not opened.")
return@Runnable
}
Log.d(LOG_TAG, "permission UI did not take focus; opening Health Connect settings fallback.")
openSettingsFallback(context, activity, "settingsFallback")
}
fallbackOpenRunnable = runnable
mainHandler.postDelayed(runnable, FALLBACK_OPEN_DELAY_MS)
}
private fun cancelFallbackOpen() {
fallbackOpenRunnable?.let(mainHandler::removeCallbacks)
fallbackOpenRunnable = null
}
private fun openSettingsFallback(context: Context, activity: Activity, outcome: String) {
cancelFallbackOpen()
val pendingResult = pendingPermissionResult
pendingPermissionResult = null
try {
activity.startActivity(settingsIntent())
Log.d(LOG_TAG, "Health Connect settings fallback opened(outcome=$outcome).")
mainScope.launch {
try {
pendingResult?.success(
connectionStateMap(
context,
permissionRequestOutcome = outcome,
),
)
} catch (stateError: Throwable) {
Log.e(LOG_TAG, "connection state failed after settings fallback.", stateError)
pendingResult?.error("health_connect_state_failed", stateError.message, null)
}
}
} catch (error: Throwable) {
Log.e(LOG_TAG, "Health Connect settings fallback failed.", error)
pendingResult?.error("health_connect_permission_request_failed", error.message, null)
}
}
private fun openSettings(context: Context, result: MethodChannel.Result) {
val activity = activityRef?.get()
if (activity == null) {
result.success(false)
return
}
val intents =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
listOf(settingsIntent())
} else if (availability(context) == "providerUpdateRequired") {
listOf(providerUpdateIntent(context), settingsIntent())
} else {
listOf(settingsIntent())
}
val opened = intents.any { intent ->
try {
Log.d(LOG_TAG, "openSettings launching ${intent.action}.")
activity.startActivity(intent)
Log.d(LOG_TAG, "openSettings launch ok.")
true
} catch (error: ActivityNotFoundException) {
Log.e(LOG_TAG, "openSettings activity not found.", error)
false
} catch (error: SecurityException) {
Log.e(LOG_TAG, "openSettings security failure.", error)
false
}
}
result.success(opened)
}
private fun providerUpdateIntent(context: Context): Intent {
val uri =
"market://details?id=$HEALTH_CONNECT_PROVIDER_PACKAGE&url=healthconnect%3A%2F%2Fonboarding"
return Intent(Intent.ACTION_VIEW).apply {
setPackage(PLAY_STORE_PACKAGE)
data = Uri.parse(uri)
putExtra("overlay", true)
putExtra("callerId", context.packageName)
}
}
private fun settingsIntent(): Intent =
Intent(HealthConnectClient.ACTION_HEALTH_CONNECT_SETTINGS)
private fun exportWorkout(
context: Context,
arguments: Any?,
result: MethodChannel.Result,
) {
val map = arguments as? Map<*, *>
if (map == null) {
result.error("invalid_health_connect_workout", "Workout payload must be a map.", null)
return
}
mainScope.launch {
try {
if (availability(context) != "available") {
result.error("health_connect_unavailable", "Health Connect is unavailable.", null)
return@launch
}
val grantedPermissions =
HealthConnectClient.getOrCreate(context)
.permissionController
.getGrantedPermissions()
if (!grantedPermissions.containsAll(requiredPermissions)) {
result.error(
"health_connect_permissions_required",
"Health Connect write permissions are not granted.",
null,
)
return@launch
}
val records = recordsFromWorkoutPayload(map)
HealthConnectClient.getOrCreate(context).insertRecords(records)
result.success(null)
} catch (error: Throwable) {
result.error("health_connect_export_failed", error.message, null)
}
}
}
private fun recordsFromWorkoutPayload(map: Map<*, *>): List<Record> {
val historyId = nonBlankString(map["historyId"], "historyId")
val title = map["title"] as? String ?: "GameTime Basketball"
val start = epochMs(map["startedAtEpochMs"], "startedAtEpochMs")
val end = epochMs(map["endedAtEpochMs"], "endedAtEpochMs")
if (!end.isAfter(start)) {
throw IllegalArgumentException("Workout end time must be after start time.")
}
val records = mutableListOf<Record>(
ExerciseSessionRecord(
startTime = start,
startZoneOffset = ZoneOffset.UTC,
endTime = end,
endZoneOffset = ZoneOffset.UTC,
exerciseType = ExerciseSessionRecord.EXERCISE_TYPE_BASKETBALL,
title = title,
notes = "Exported from GameTime.",
metadata = Metadata.manualEntry(
clientRecordId = clientRecordId(historyId, "exercise-session"),
),
),
)
val distanceMeters = positiveDouble(map["totalDistanceMeters"])
if (distanceMeters != null) {
records += DistanceRecord(
startTime = start,
startZoneOffset = ZoneOffset.UTC,
endTime = end,
endZoneOffset = ZoneOffset.UTC,
distance = Length.meters(distanceMeters),
metadata = Metadata.manualEntry(
clientRecordId = clientRecordId(historyId, "distance"),
),
)
}
val caloriesKcal = positiveDouble(map["totalCaloriesKcal"])
if (caloriesKcal != null) {
records += TotalCaloriesBurnedRecord(
startTime = start,
startZoneOffset = ZoneOffset.UTC,
endTime = end,
endZoneOffset = ZoneOffset.UTC,
energy = Energy.kilocalories(caloriesKcal),
metadata = Metadata.manualEntry(
clientRecordId = clientRecordId(historyId, "calories"),
),
)
}
val heartRateSamples = heartRateSamples(map["heartRateSamples"], start, end)
if (heartRateSamples.isNotEmpty()) {
records += HeartRateRecord(
startTime = start,
startZoneOffset = ZoneOffset.UTC,
endTime = end,
endZoneOffset = ZoneOffset.UTC,
samples = heartRateSamples,
metadata = Metadata.manualEntry(
clientRecordId = clientRecordId(historyId, "heart-rate"),
),
)
}
return records
}
private fun nonBlankString(value: Any?, fieldName: String): String {
val text = (value as? String)?.trim()
if (text.isNullOrEmpty()) {
throw IllegalArgumentException("$fieldName must be a non-blank string.")
}
return text
}
private fun clientRecordId(historyId: String, kind: String): String =
"gametime:$historyId:$kind"
private fun heartRateSamples(
payload: Any?,
start: Instant,
end: Instant,
): List<HeartRateRecord.Sample> {
val items = payload as? List<*> ?: return emptyList()
return items.mapNotNull { item ->
val map = item as? Map<*, *> ?: return@mapNotNull null
val time =
(map["timeEpochMs"] as? Number)?.toLong()?.let(Instant::ofEpochMilli)
?: return@mapNotNull null
val beatsPerMinute = (map["beatsPerMinute"] as? Number)?.toLong()
?: return@mapNotNull null
if (beatsPerMinute <= 0 || time.isBefore(start) || time.isAfter(end)) {
return@mapNotNull null
}
HeartRateRecord.Sample(time = time, beatsPerMinute = beatsPerMinute)
}
}
private fun epochMs(value: Any?, fieldName: String): Instant {
val epochMs = (value as? Number)?.toLong()
?: throw IllegalArgumentException("$fieldName must be an epoch millisecond number.")
return Instant.ofEpochMilli(epochMs)
}
private fun positiveDouble(value: Any?): Double? {
val number = (value as? Number)?.toDouble() ?: return null
return if (number > 0.0) number else null
}
}

View File

@ -0,0 +1,40 @@
package com.idea.gametime.health
import android.app.Activity
import android.os.Bundle
import android.view.ViewGroup
import android.widget.ScrollView
import android.widget.TextView
class HealthConnectRationaleActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = "Health Connect"
val textView =
TextView(this).apply {
text =
"""
GameTime utilise Health Connect pour enregistrer les donnees compatibles de vos seances de basketball.
Les donnees envoyees peuvent inclure la seance d'exercice, la distance, les calories et les mesures de frequence cardiaque disponibles.
GameTime n'utilise ces donnees que pour le suivi sportif affiche dans l'application et dans Health Connect. Vous pouvez modifier ou retirer ces permissions a tout moment depuis Health Connect.
""".trimIndent()
textSize = 16f
setPadding(48, 48, 48, 48)
}
val scrollView =
ScrollView(this).apply {
addView(
textView,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
),
)
}
setContentView(scrollView)
}
}

View File

@ -1,4 +1,4 @@
package com.gametime.app.session
package com.idea.gametime.session
import android.Manifest
import android.app.Activity

View File

@ -1,4 +1,4 @@
package com.gametime.app.session
package com.idea.gametime.session
import android.app.Notification
import android.app.NotificationChannel
@ -8,8 +8,8 @@ import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import com.gametime.app.MainActivity
import com.gametime.app.R
import com.idea.gametime.MainActivity
import com.idea.gametime.R
class SessionStatusForegroundService : Service() {
override fun onCreate() {
@ -62,7 +62,7 @@ class SessionStatusForegroundService : Service() {
Notification.BigTextStyle().bigText("$primaryLine\n$it")
}
return builder
.setSmallIcon(R.mipmap.ic_launcher)
.setSmallIcon(R.drawable.ic_ongoing_gt)
.setContentTitle(title)
.setContentText(primaryLine)
.setSubText(secondaryLine)
@ -89,7 +89,7 @@ class SessionStatusForegroundService : Service() {
}
companion object {
const val ACTION_SHOW = "com.gametime.app.session.SHOW"
const val ACTION_SHOW = "com.idea.gametime.session.SHOW"
const val EXTRA_TITLE = "title"
const val EXTRA_PRIMARY_LINE = "primaryLine"
const val EXTRA_SECONDARY_LINE = "secondaryLine"

View File

@ -1,4 +1,4 @@
package com.gametime.app.watch
package com.idea.gametime.watch
import com.google.android.gms.wearable.CapabilityInfo
import com.google.android.gms.wearable.MessageEvent

View File

@ -1,4 +1,4 @@
package com.gametime.app.watch
package com.idea.gametime.watch
import android.content.Context
import android.content.Intent
@ -214,32 +214,7 @@ object WatchBridgePlugin {
sendMessage(context, targetNode, ACK_PATH, payload, result)
return
}
Wearable.getCapabilityClient(context)
.getCapability(WATCH_CAPABILITY, CapabilityClient.FILTER_REACHABLE)
.addOnSuccessListener { capability ->
val nodes = capability.nodes.toList()
if (nodes.isEmpty()) {
result.success(null)
return@addOnSuccessListener
}
var remaining = nodes.size
var failed = false
for (node in nodes) {
Wearable.getMessageClient(context)
.sendMessage(node.id, ACK_PATH, payload)
.addOnSuccessListener {
remaining -= 1
if (remaining == 0 && !failed) result.success(null)
}
.addOnFailureListener { error ->
failed = true
result.error("send_ack_failed", error.message, null)
}
}
}
.addOnFailureListener { error ->
result.error("capability_lookup_failed", error.message, null)
}
result.success(null)
}
private fun sendMessage(

View File

@ -1,4 +1,4 @@
package com.gametime.app.watch
package com.idea.gametime.watch
import android.app.Notification
import android.app.NotificationChannel
@ -7,7 +7,7 @@ import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import com.gametime.app.R
import com.idea.gametime.R
class WatchCompanionForegroundService : Service() {
override fun onCreate() {
@ -30,7 +30,7 @@ class WatchCompanionForegroundService : Service() {
Notification.Builder(this)
}
return builder
.setSmallIcon(R.mipmap.ic_launcher)
.setSmallIcon(R.drawable.ic_ongoing_gt)
.setContentTitle("GameTime")
.setContentText("Séance en cours")
.setOngoing(true)

View File

@ -4,4 +4,6 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<application android:usesCleartextTraffic="true"/>
</manifest>