Remove WearOS variant (hasn't been supported for quite some time).
@@ -67,7 +67,6 @@ jobs:
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
app/build/outputs/apk/foss/beta/*.apk
|
||||
app-wear/build/outputs/apk/foss/beta/*.apk
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -130,14 +129,6 @@ jobs:
|
||||
ruby-version: 3.3.6
|
||||
bundler-cache: true
|
||||
|
||||
# - name: Assemble WearOS beta and upload to Google Play
|
||||
# if: contains(steps.tagger.outputs.tag, '-beta')
|
||||
# run: bundle exec fastlane beta_wearos
|
||||
# env:
|
||||
# STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||
# KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||
# KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||
|
||||
- name: Assemble beta and upload to Google Play
|
||||
if: contains(steps.tagger.outputs.tag, '-beta')
|
||||
run: bundle exec fastlane beta
|
||||
@@ -146,14 +137,6 @@ jobs:
|
||||
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||
|
||||
# - name: Assemble WearOS production and upload to Google Play
|
||||
# if: "!contains(steps.tagger.outputs.tag, '-beta')"
|
||||
# run: bundle exec fastlane production_wearos
|
||||
# env:
|
||||
# STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
|
||||
# KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||
# KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||
|
||||
- name: Assemble production and upload to Google Play
|
||||
if: "!contains(steps.tagger.outputs.tag, '-beta')"
|
||||
run: bundle exec fastlane production
|
||||
|
||||
@@ -65,8 +65,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
### Multi-Module Structure
|
||||
|
||||
- **app/**: Main Android application with FOSS and Google Play flavors
|
||||
- **app-common/**: Shared code between main app and Wear OS
|
||||
- **app-wear/**: Dedicated Wear OS companion application
|
||||
- **app-common/**: Shared code between main app
|
||||
|
||||
### Core Architecture Patterns
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ A companion app that adds support for AirPod specific features to Android:
|
||||
* Ear detection with automatic play/pause.
|
||||
* Automatically connect phone & AirPods.
|
||||
* Show popup when case is opened.
|
||||
* Support for Wear OS
|
||||
* Widgets
|
||||
|
||||
CAPod is ad-free. Some additional features require an in-app purchase.
|
||||
|
||||
@@ -25,6 +25,5 @@ exclude:
|
||||
- crowdin*
|
||||
- app
|
||||
- app-common
|
||||
- app-wear
|
||||
- CONTRIBUTING.md
|
||||
- CLAUDE.md
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,140 +0,0 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
id("kotlin-kapt")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
apply(plugin = "dagger.hilt.android.plugin")
|
||||
apply(plugin = "androidx.navigation.safeargs.kotlin")
|
||||
|
||||
android {
|
||||
compileSdk = ProjectConfig.compileSdk
|
||||
namespace = ProjectConfig.packageName
|
||||
|
||||
defaultConfig {
|
||||
applicationId = ProjectConfig.packageName
|
||||
|
||||
minSdk = ProjectConfig.minSdk
|
||||
targetSdk = ProjectConfig.targetSdk
|
||||
|
||||
versionCode = ProjectConfig.Version.code + 1 // Wear app
|
||||
versionName = ProjectConfig.Version.name
|
||||
|
||||
testInstrumentationRunner = "eu.darken.capod.HiltTestRunner"
|
||||
|
||||
buildConfigField("String", "GITSHA", "\"${lastCommitHash()}\"")
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
val basePath = File(System.getProperty("user.home"), ".appconfig/${ProjectConfig.packageName}")
|
||||
create("releaseFoss") {
|
||||
setupCredentials(File(basePath, "signing-foss.properties"))
|
||||
}
|
||||
create("releaseGplay") {
|
||||
setupCredentials(File(basePath, "signing-gplay-upload.properties"))
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions.add("version")
|
||||
productFlavors {
|
||||
create("foss") {
|
||||
dimension = "version"
|
||||
signingConfig = signingConfigs["releaseFoss"]
|
||||
}
|
||||
create("gplay") {
|
||||
dimension = "version"
|
||||
signingConfig = signingConfigs["releaseGplay"]
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
val customProguardRules = fileTree(File("../proguard")) {
|
||||
include("*.pro")
|
||||
}
|
||||
debug {
|
||||
isMinifyEnabled = false
|
||||
isShrinkResources = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
proguardFiles(*customProguardRules.toList().toTypedArray())
|
||||
proguardFiles("proguard-rules-debug.pro")
|
||||
}
|
||||
create("beta") {
|
||||
lint {
|
||||
abortOnError = true
|
||||
fatal.add("StopShip")
|
||||
}
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
proguardFiles(*customProguardRules.toList().toTypedArray())
|
||||
}
|
||||
release {
|
||||
lint {
|
||||
abortOnError = true
|
||||
fatal.add("StopShip")
|
||||
}
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
proguardFiles(*customProguardRules.toList().toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
buildOutputs.all {
|
||||
val variantOutputImpl = this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
val variantName: String = variantOutputImpl.name
|
||||
|
||||
if (listOf("release", "beta").any { variantName.lowercase().contains(it) }) {
|
||||
val outputFileName = ProjectConfig.packageName + "-WEAROS" +
|
||||
"-v${defaultConfig.versionName}-${defaultConfig.versionCode}" +
|
||||
"-${variantName.uppercase()}.apk"
|
||||
|
||||
variantOutputImpl.outputFileName = outputFileName
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = freeCompilerArgs + listOf(
|
||||
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||
"-opt-in=kotlin.time.ExperimentalTime",
|
||||
"-opt-in=kotlin.RequiresOptIn"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||
|
||||
implementation(project(":app-common"))
|
||||
|
||||
addBaseKotlin()
|
||||
|
||||
addDagger()
|
||||
|
||||
addBaseWorkManager()
|
||||
|
||||
addBaseAndroid()
|
||||
addBaseAndroidUi()
|
||||
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
|
||||
addNavigation()
|
||||
|
||||
addTesting()
|
||||
|
||||
implementation("androidx.wear:wear:1.2.0")
|
||||
implementation("androidx.wear.tiles:tiles-material:1.1.0")
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
# 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
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="eu.darken.capod">
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:name=".App"
|
||||
android:theme="@style/AppTheme">
|
||||
<uses-library
|
||||
android:name="com.google.android.wearable"
|
||||
android:required="true" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.wearable.standalone"
|
||||
android:value="true" />
|
||||
|
||||
<activity
|
||||
android:name=".wear.ui.MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppThemeSplash"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,85 +0,0 @@
|
||||
package eu.darken.capod
|
||||
|
||||
import android.app.Application
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import eu.darken.capod.common.BuildConfig
|
||||
import eu.darken.capod.common.BuildConfigWrap
|
||||
import eu.darken.capod.common.coroutine.AppScope
|
||||
import eu.darken.capod.common.debug.autoreport.AutomaticBugReporter
|
||||
import eu.darken.capod.common.debug.logging.LogCatLogger
|
||||
import eu.darken.capod.common.debug.logging.Logging
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.asLog
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.wear.core.MonitorWorker
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import java.time.Duration
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidApp
|
||||
open class App : Application(), Configuration.Provider {
|
||||
|
||||
@Inject lateinit var workerFactory: HiltWorkerFactory
|
||||
@Inject lateinit var autoReporting: AutomaticBugReporter
|
||||
@Inject lateinit var generalSettings: GeneralSettings
|
||||
@Inject lateinit var workManager: WorkManager
|
||||
@Inject @AppScope lateinit var appScope: CoroutineScope
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (BuildConfig.DEBUG) Logging.install(LogCatLogger())
|
||||
|
||||
autoReporting.setup(this)
|
||||
|
||||
setupWorker()
|
||||
|
||||
log(TAG) { "onCreate() done! ${Exception().asLog()}" }
|
||||
}
|
||||
|
||||
private fun setupWorker() {
|
||||
log(TAG) { "setupWorker()" }
|
||||
val workRequest = PeriodicWorkRequestBuilder<MonitorWorker>(
|
||||
Duration.ofMinutes(30),
|
||||
Duration.ofMinutes(30)
|
||||
).apply {
|
||||
setInputData(Data.Builder().build())
|
||||
}.build()
|
||||
|
||||
log(TAG, VERBOSE) { "Worker request: $workRequest" }
|
||||
|
||||
val operation = workManager.enqueueUniquePeriodicWork(
|
||||
"${BuildConfigWrap.APPLICATION_ID}.monitor.worker",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
workRequest,
|
||||
)
|
||||
|
||||
operation.result.get()
|
||||
log(TAG) { "Monitor start request send." }
|
||||
}
|
||||
|
||||
override val workManagerConfiguration: Configuration
|
||||
get() = Configuration.Builder()
|
||||
.setMinimumLoggingLevel(
|
||||
when {
|
||||
BuildConfigWrap.DEBUG -> android.util.Log.VERBOSE
|
||||
BuildConfigWrap.BUILD_TYPE == BuildConfigWrap.BuildType.DEV -> android.util.Log.DEBUG
|
||||
BuildConfigWrap.BUILD_TYPE == BuildConfigWrap.BuildType.BETA -> android.util.Log.INFO
|
||||
BuildConfigWrap.BUILD_TYPE == BuildConfigWrap.BuildType.RELEASE -> android.util.Log.WARN
|
||||
else -> android.util.Log.VERBOSE
|
||||
}
|
||||
)
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
internal val TAG = logTag("CAP")
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package eu.darken.capod.wear.core
|
||||
|
||||
import android.content.Context
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.monitor.core.MonitorCoroutineScope
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.take
|
||||
|
||||
|
||||
@HiltWorker
|
||||
class MonitorWorker @AssistedInject constructor(
|
||||
@Assisted private val context: Context,
|
||||
@Assisted private val params: WorkerParameters,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val permissionTool: PermissionTool,
|
||||
private val podMonitor: PodMonitor,
|
||||
) : CoroutineWorker(context, params) {
|
||||
|
||||
private val workerScope = MonitorCoroutineScope()
|
||||
|
||||
private var finishedWithError = false
|
||||
|
||||
init {
|
||||
log(TAG, VERBOSE) { "init(): workerId=$id" }
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result = try {
|
||||
val start = System.currentTimeMillis()
|
||||
log(TAG, VERBOSE) { "Executing $inputData now (runAttemptCount=$runAttemptCount)" }
|
||||
|
||||
doDoWork()
|
||||
|
||||
val duration = System.currentTimeMillis() - start
|
||||
|
||||
log(TAG, VERBOSE) { "Execution finished after ${duration}ms, $inputData" }
|
||||
|
||||
Result.success(inputData)
|
||||
} catch (e: Throwable) {
|
||||
if (e !is CancellationException) {
|
||||
Bugs.report(tag = TAG, "Execution failed", exception = e)
|
||||
finishedWithError = true
|
||||
Result.failure(inputData)
|
||||
} else {
|
||||
Result.success()
|
||||
}
|
||||
} finally {
|
||||
this.workerScope.cancel("Worker finished (withError?=$finishedWithError).")
|
||||
}
|
||||
|
||||
private suspend fun doDoWork() = withContext(dispatcherProvider.IO) {
|
||||
val permissionsMissingOnStart = permissionTool.missingPermissions.first()
|
||||
if (permissionsMissingOnStart.isNotEmpty()) {
|
||||
log(TAG, WARN) { "Aborting, missing permissions: $permissionsMissingOnStart" }
|
||||
return@withContext
|
||||
}
|
||||
|
||||
val monitorJob = podMonitor.mainDevice
|
||||
.filterNotNull()
|
||||
.take(3)
|
||||
.setupCommonEventHandlers(TAG) { "monitorJob" }
|
||||
.launchIn(workerScope)
|
||||
|
||||
try {
|
||||
withTimeout(15 * 1000) {
|
||||
monitorJob.join()
|
||||
}
|
||||
log(TAG) { "Monitor job quit after a few takes." }
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
log(TAG) { "Monitor job quit after finding nothing." }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = logTag("Monitor", "Worker")
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package eu.darken.capod.wear.core
|
||||
|
||||
import android.content.Context
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.Locale
|
||||
|
||||
data class UserTime(
|
||||
val time: Instant = Instant.now(),
|
||||
) {
|
||||
fun toFormatted(context: Context): String = DateTimeFormatter
|
||||
.ofLocalizedTime(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
.withZone(ZoneId.systemDefault())
|
||||
.format(time)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package eu.darken.capod.wear.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.wear.widget.SwipeDismissFrameLayout
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.navigation.findNavController
|
||||
import eu.darken.capod.common.uix.Activity2
|
||||
import eu.darken.capod.databinding.MainActivityBinding
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : Activity2() {
|
||||
|
||||
private val vm: MainActivityVM by viewModels()
|
||||
private lateinit var ui: MainActivityBinding
|
||||
private val navController by lazy { supportFragmentManager.findNavController(R.id.nav_host) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
ui = MainActivityBinding.inflate(layoutInflater)
|
||||
setContentView(ui.root)
|
||||
|
||||
ui.swipeWrapper.addCallback(object : SwipeDismissFrameLayout.Callback() {
|
||||
override fun onDismissed(layout: SwipeDismissFrameLayout) {
|
||||
if (!navController.popBackStack()) finish()
|
||||
super.onDismissed(layout)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package eu.darken.capod.wear.ui
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.common.uix.ViewModel2
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class MainActivityVM @Inject constructor(
|
||||
handle: SavedStateHandle,
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
private val podMonitor: PodMonitor,
|
||||
private val permissionTool: PermissionTool,
|
||||
) : ViewModel2(dispatcherProvider = dispatcherProvider) {
|
||||
|
||||
sealed class State {
|
||||
data class PermissionRequired(
|
||||
val permissions: List<Permission>,
|
||||
) : State()
|
||||
|
||||
data class Devices(
|
||||
val devices: List<PodDevice>
|
||||
) : State()
|
||||
}
|
||||
|
||||
val state = combine(
|
||||
podMonitor.devices,
|
||||
permissionTool.missingPermissions,
|
||||
) { devices, missingPermissions ->
|
||||
when {
|
||||
missingPermissions.isNotEmpty() -> State.PermissionRequired(
|
||||
permissions = missingPermissions.toList()
|
||||
)
|
||||
else -> State.Devices(
|
||||
devices = devices
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import eu.darken.capod.common.lists.BindableVH
|
||||
import eu.darken.capod.common.lists.differ.AsyncDiffer
|
||||
import eu.darken.capod.common.lists.differ.DifferItem
|
||||
import eu.darken.capod.common.lists.differ.HasAsyncDiffer
|
||||
import eu.darken.capod.common.lists.differ.setupDiffer
|
||||
import eu.darken.capod.common.lists.modular.ModularAdapter
|
||||
import eu.darken.capod.common.lists.modular.mods.DataBinderMod
|
||||
import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod
|
||||
import eu.darken.capod.wear.ui.overview.cards.BluetoothDisabledVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.MissingMainDeviceVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.PermissionCardVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.pods.DualPodsCardVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.pods.SinglePodsCardVH
|
||||
import javax.inject.Inject
|
||||
|
||||
class OverviewAdapter @Inject constructor() :
|
||||
ModularAdapter<OverviewAdapter.BaseVH<OverviewAdapter.Item, ViewBinding>>(),
|
||||
HasAsyncDiffer<OverviewAdapter.Item> {
|
||||
|
||||
override val asyncDiffer: AsyncDiffer<*, Item> = setupDiffer()
|
||||
|
||||
init {
|
||||
modules.add(DataBinderMod(data))
|
||||
modules.add(TypedVHCreatorMod({ data[it] is PermissionCardVH.Item }) { PermissionCardVH(it) })
|
||||
modules.add(TypedVHCreatorMod({ data[it] is DualPodsCardVH.Item }) { DualPodsCardVH(it) })
|
||||
modules.add(TypedVHCreatorMod({ data[it] is SinglePodsCardVH.Item }) { SinglePodsCardVH(it) })
|
||||
modules.add(TypedVHCreatorMod({ data[it] is MissingMainDeviceVH.Item }) { MissingMainDeviceVH(it) })
|
||||
modules.add(TypedVHCreatorMod({ data[it] is BluetoothDisabledVH.Item }) { BluetoothDisabledVH(it) })
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
abstract class BaseVH<D : Item, B : ViewBinding>(
|
||||
@LayoutRes layoutId: Int,
|
||||
parent: ViewGroup
|
||||
) : ModularAdapter.VH(layoutId, parent), BindableVH<D, B>
|
||||
|
||||
interface Item : DifferItem
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.lists.differ.update
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.common.uix.Fragment3
|
||||
import eu.darken.capod.common.viewbinding.viewBinding
|
||||
import eu.darken.capod.databinding.MainFragmentBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class OverviewFragment : Fragment3(R.layout.main_fragment) {
|
||||
|
||||
override val vm: OverviewFragmentVM by viewModels()
|
||||
override val ui: MainFragmentBinding by viewBinding()
|
||||
|
||||
@Inject
|
||||
lateinit var overviewAdapter: OverviewAdapter
|
||||
|
||||
lateinit var permissionLauncher: ActivityResultLauncher<String>
|
||||
var awaitingPermission = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
awaitingPermission = savedInstanceState?.getBoolean("awaitingPermission") ?: false
|
||||
|
||||
permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
log { "Request for $id was granted=$granted" }
|
||||
vm.onPermissionResult(granted)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
ui.list.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
this.adapter = overviewAdapter
|
||||
}
|
||||
|
||||
vm.listItems.observe2(ui) { overviewAdapter.update(it) }
|
||||
|
||||
vm.requestPermissionEvent.observe2(ui) {
|
||||
when (it) {
|
||||
Permission.IGNORE_BATTERY_OPTIMIZATION -> {
|
||||
awaitingPermission = true
|
||||
startActivity(
|
||||
Intent(
|
||||
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
Uri.parse("package:${requireContext().packageName}")
|
||||
)
|
||||
)
|
||||
}
|
||||
Permission.SYSTEM_ALERT_WINDOW -> {
|
||||
awaitingPermission = true
|
||||
startActivity(
|
||||
Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:${requireContext().packageName}")
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
permissionLauncher.launch(it.permissionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean("awaitingPermission", awaitingPermission)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (awaitingPermission) {
|
||||
awaitingPermission = false
|
||||
log { "awaitingPermission=true" }
|
||||
vm.onPermissionResult(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.flow.combine
|
||||
import eu.darken.capod.common.flow.throttleLatest
|
||||
import eu.darken.capod.common.livedata.SingleLiveEvent
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.common.uix.ViewModel3
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.pods.core.DualPodDevice
|
||||
import eu.darken.capod.pods.core.SinglePodDevice
|
||||
import eu.darken.capod.wear.core.UserTime
|
||||
import eu.darken.capod.wear.ui.overview.cards.BluetoothDisabledVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.MissingMainDeviceVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.PermissionCardVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.pods.DualPodsCardVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.pods.SinglePodsCardVH
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.isActive
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class OverviewFragmentVM @Inject constructor(
|
||||
@Suppress("UNUSED_PARAMETER") handle: SavedStateHandle,
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
private val podMonitor: PodMonitor,
|
||||
private val permissionTool: PermissionTool,
|
||||
debugSettings: DebugSettings,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
) : ViewModel3(dispatcherProvider = dispatcherProvider) {
|
||||
|
||||
private val updateTicker = channelFlow<Unit> {
|
||||
while (isActive) {
|
||||
trySend(Unit)
|
||||
delay(3000)
|
||||
}
|
||||
}
|
||||
|
||||
val requestPermissionEvent = SingleLiveEvent<Permission>()
|
||||
|
||||
private val mainDevice = podMonitor.mainDevice.throttleLatest(1000)
|
||||
|
||||
val listItems: LiveData<List<OverviewAdapter.Item>> = combine(
|
||||
updateTicker,
|
||||
permissionTool.missingPermissions,
|
||||
debugSettings.isDebugModeEnabled.flow,
|
||||
bluetoothManager.isBluetoothEnabled,
|
||||
mainDevice,
|
||||
) { _, permissions, isDebugMode, isBluetoothEnabled, _ ->
|
||||
val items = mutableListOf<OverviewAdapter.Item>()
|
||||
|
||||
if (permissions.isNotEmpty()) {
|
||||
permissions
|
||||
.map { perm ->
|
||||
PermissionCardVH.Item(
|
||||
permission = perm,
|
||||
onRequest = { requestPermissionEvent.postValue(it) },
|
||||
)
|
||||
}
|
||||
.run { items.addAll(this) }
|
||||
return@combine items
|
||||
}
|
||||
|
||||
if (!isBluetoothEnabled) {
|
||||
items.add(0, BluetoothDisabledVH.Item)
|
||||
return@combine items
|
||||
}
|
||||
|
||||
val podToShow = podMonitor.latestMainDevice()
|
||||
log(TAG, VERBOSE) { "Showing $podToShow" }
|
||||
|
||||
val now = Instant.now()
|
||||
|
||||
val pod = when (podToShow) {
|
||||
is DualPodDevice -> DualPodsCardVH.Item(
|
||||
now = now,
|
||||
device = podToShow,
|
||||
showDebug = isDebugMode,
|
||||
isMainPod = true,
|
||||
userTime = UserTime(),
|
||||
)
|
||||
|
||||
is SinglePodDevice -> SinglePodsCardVH.Item(
|
||||
now = now,
|
||||
device = podToShow,
|
||||
showDebug = isDebugMode,
|
||||
isMainPod = true,
|
||||
userTime = UserTime(),
|
||||
)
|
||||
|
||||
else -> MissingMainDeviceVH.Item(
|
||||
userTime = UserTime(),
|
||||
)
|
||||
}
|
||||
items.add(pod)
|
||||
|
||||
items
|
||||
}
|
||||
.catch { errorEvents.postValue(it) }
|
||||
.asLiveData2()
|
||||
|
||||
fun onPermissionResult(granted: Boolean) {
|
||||
if (granted) permissionTool.recheck()
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview.cards
|
||||
|
||||
import android.view.ViewGroup
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.lists.binding
|
||||
import eu.darken.capod.common.lists.differ.DifferItem
|
||||
import eu.darken.capod.databinding.OverviewBluetoothDisabledItemBinding
|
||||
import eu.darken.capod.wear.ui.overview.OverviewAdapter
|
||||
|
||||
class BluetoothDisabledVH(parent: ViewGroup) :
|
||||
OverviewAdapter.BaseVH<BluetoothDisabledVH.Item, OverviewBluetoothDisabledItemBinding>(
|
||||
R.layout.overview_bluetooth_disabled_item,
|
||||
parent
|
||||
) {
|
||||
|
||||
override val viewBinding = lazy {
|
||||
OverviewBluetoothDisabledItemBinding.bind(itemView)
|
||||
}
|
||||
|
||||
override val onBindData: OverviewBluetoothDisabledItemBinding.(
|
||||
item: Item,
|
||||
payloads: List<Any>
|
||||
) -> Unit = binding(payload = true) { item ->
|
||||
|
||||
}
|
||||
|
||||
object Item : OverviewAdapter.Item {
|
||||
override val stableId: Long = Item::class.hashCode().toLong()
|
||||
|
||||
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
|
||||
get() = { old, new -> if (new::class.isInstance(old)) new else null }
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview.cards
|
||||
|
||||
import android.view.ViewGroup
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.lists.binding
|
||||
import eu.darken.capod.common.lists.differ.DifferItem
|
||||
import eu.darken.capod.databinding.OverviewNomaindeviceItemBinding
|
||||
import eu.darken.capod.wear.core.UserTime
|
||||
import eu.darken.capod.wear.ui.overview.OverviewAdapter
|
||||
|
||||
class MissingMainDeviceVH(parent: ViewGroup) :
|
||||
OverviewAdapter.BaseVH<MissingMainDeviceVH.Item, OverviewNomaindeviceItemBinding>(
|
||||
R.layout.overview_nomaindevice_item,
|
||||
parent
|
||||
) {
|
||||
|
||||
override val viewBinding = lazy {
|
||||
OverviewNomaindeviceItemBinding.bind(itemView)
|
||||
}
|
||||
|
||||
override val onBindData: OverviewNomaindeviceItemBinding.(
|
||||
item: Item,
|
||||
payloads: List<Any>
|
||||
) -> Unit = binding(payload = true) { item ->
|
||||
userTime.text = item.userTime.toFormatted(context)
|
||||
|
||||
}
|
||||
|
||||
data class Item(
|
||||
val userTime: UserTime,
|
||||
) : OverviewAdapter.Item {
|
||||
override val stableId: Long = this.hashCode().toLong()
|
||||
|
||||
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
|
||||
get() = { old, new -> if (new::class.isInstance(old)) new else null }
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview.cards
|
||||
|
||||
import android.view.ViewGroup
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.lists.binding
|
||||
import eu.darken.capod.common.lists.differ.DifferItem
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.databinding.OverviewPermissionItemBinding
|
||||
import eu.darken.capod.wear.ui.overview.OverviewAdapter
|
||||
|
||||
class PermissionCardVH(parent: ViewGroup) :
|
||||
OverviewAdapter.BaseVH<PermissionCardVH.Item, OverviewPermissionItemBinding>(
|
||||
R.layout.overview_permission_item,
|
||||
parent
|
||||
) {
|
||||
|
||||
override val viewBinding = lazy {
|
||||
OverviewPermissionItemBinding.bind(itemView)
|
||||
}
|
||||
|
||||
override val onBindData: OverviewPermissionItemBinding.(
|
||||
item: Item,
|
||||
payloads: List<Any>
|
||||
) -> Unit = binding(payload = true) { item ->
|
||||
permissionLabel.setText(item.permission.labelRes)
|
||||
permissionDescription.setText(item.permission.descriptionRes)
|
||||
grantAction.setOnClickListener { item.onRequest(item.permission) }
|
||||
}
|
||||
|
||||
data class Item(
|
||||
val permission: Permission,
|
||||
val onRequest: (Permission) -> Unit
|
||||
) : OverviewAdapter.Item {
|
||||
override val stableId: Long = permission.hashCode().toLong()
|
||||
|
||||
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
|
||||
get() = { old, new -> if (new::class.isInstance(old)) new else null }
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview.cards.pods
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.lists.binding
|
||||
import eu.darken.capod.databinding.OverviewPodsDualItemBinding
|
||||
import eu.darken.capod.pods.core.*
|
||||
import eu.darken.capod.pods.core.apple.DualApplePods
|
||||
import eu.darken.capod.wear.core.UserTime
|
||||
import java.time.Instant
|
||||
|
||||
class DualPodsCardVH(parent: ViewGroup) :
|
||||
PodDeviceVH<DualPodsCardVH.Item, OverviewPodsDualItemBinding>(
|
||||
R.layout.overview_pods_dual_item,
|
||||
parent
|
||||
) {
|
||||
|
||||
override val viewBinding = lazy { OverviewPodsDualItemBinding.bind(itemView) }
|
||||
|
||||
override val onBindData = binding(payload = true) { item: Item ->
|
||||
userTime.text = item.userTime.toFormatted(context)
|
||||
|
||||
val device = item.device
|
||||
|
||||
name.apply {
|
||||
val sb = StringBuilder(device.getLabel(context))
|
||||
if (device is HasPodStyle && item.showDebug) {
|
||||
val style = device.podStyle
|
||||
sb.append(" (${style.getColor(context)})")
|
||||
}
|
||||
text = sb
|
||||
|
||||
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||
else setTypeface(null, Typeface.NORMAL)
|
||||
|
||||
if (device is DualApplePods && item.showDebug) {
|
||||
append(" [${device.primaryPod.name}]")
|
||||
}
|
||||
}
|
||||
deviceIcon.setImageResource(device.iconRes)
|
||||
podLeftIcon.setImageResource(device.leftPodIcon)
|
||||
podRightIcon.setImageResource(device.rightPodIcon)
|
||||
|
||||
lastSeen.text = device.lastSeenFormatted(item.now)
|
||||
|
||||
// Pods battery state
|
||||
device.apply {
|
||||
podLeftBatteryIcon.setImageResource(getBatteryDrawable(batteryLeftPodPercent))
|
||||
podLeftBatteryLabel.text = getBatteryLevelLeftPod(context)
|
||||
|
||||
podRightBatteryIcon.setImageResource(getBatteryDrawable(batteryRightPodPercent))
|
||||
podRightBatteryLabel.text = getBatteryLevelRightPod(context)
|
||||
}
|
||||
|
||||
// Pods charging state
|
||||
device.apply {
|
||||
if (this is HasChargeDetectionDual) {
|
||||
podLeftChargingIcon.isInvisible = !isLeftPodCharging
|
||||
|
||||
podRightChargingIcon.isInvisible = !isRightPodCharging
|
||||
} else {
|
||||
podLeftChargingIcon.isGone = true
|
||||
|
||||
podRightChargingIcon.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
// Microphone state
|
||||
device.apply {
|
||||
if (this is HasDualMicrophone) {
|
||||
podLeftMicrophoneIcon.isInvisible = !isLeftPodMicrophone
|
||||
|
||||
podRightMicrophoneIcon.isInvisible = !isRightPodMicrophone
|
||||
} else {
|
||||
podLeftMicrophoneIcon.isGone = true
|
||||
|
||||
podRightMicrophoneIcon.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
// Pods wear state
|
||||
device.apply {
|
||||
if (this is HasEarDetectionDual) {
|
||||
podLeftWearIcon.isInvisible = !isLeftPodInEar
|
||||
|
||||
podRightWearIcon.isInvisible = !isRightPodInEar
|
||||
} else {
|
||||
podLeftWearIcon.isGone = true
|
||||
|
||||
podRightWearIcon.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
// Case charge state
|
||||
device.apply {
|
||||
if (this is HasCase) {
|
||||
podCaseIcon.setImageResource(caseIcon)
|
||||
podCaseBatteryIcon.isGone = false
|
||||
podCaseBatteryIcon.setImageResource(getBatteryDrawable(batteryCasePercent))
|
||||
podCaseBatteryLabel.text = getBatteryLevelCase(context)
|
||||
|
||||
podCaseChargingIcon.isInvisible = !isCaseCharging
|
||||
} else {
|
||||
podCaseBatteryIcon.isGone = true
|
||||
podCaseBatteryLabel.isGone = true
|
||||
|
||||
podCaseChargingIcon.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
// Connection state
|
||||
device.apply {
|
||||
val sb = StringBuilder()
|
||||
if (this is HasStateDetection) {
|
||||
sb.append(state.getLabel(context))
|
||||
}
|
||||
if (item.showDebug) {
|
||||
sb.append("\n\n").append("---Debug---")
|
||||
sb.append("\n").append(rawDataHex)
|
||||
}
|
||||
status.text = sb
|
||||
status.isGone = sb.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
data class Item(
|
||||
override val now: Instant,
|
||||
override val device: DualPodDevice,
|
||||
override val showDebug: Boolean,
|
||||
override val isMainPod: Boolean,
|
||||
val userTime: UserTime,
|
||||
) : PodDeviceVH.Item
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview.cards.pods
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import eu.darken.capod.common.lists.BindableVH
|
||||
import eu.darken.capod.common.lists.differ.DifferItem
|
||||
import eu.darken.capod.common.lists.modular.ModularAdapter
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.getSignalQuality
|
||||
import eu.darken.capod.wear.ui.overview.OverviewAdapter
|
||||
import java.time.Instant
|
||||
|
||||
abstract class PodDeviceVH<D : PodDeviceVH.Item, B : ViewBinding>(
|
||||
@LayoutRes layoutId: Int,
|
||||
parent: ViewGroup
|
||||
) : ModularAdapter.VH(layoutId, parent), BindableVH<D, B> {
|
||||
|
||||
fun Item.getReceptionText(): String = device.getSignalQuality(context)
|
||||
.let { if (showDebug) "$it ${device.seenCounter}" else it }
|
||||
.let { if (isMainPod) "$it\n(${getString(eu.darken.capod.common.R.string.pods_yours)})" else it }
|
||||
|
||||
interface Item : OverviewAdapter.Item {
|
||||
|
||||
val now: Instant
|
||||
|
||||
val device: PodDevice
|
||||
|
||||
val showDebug: Boolean
|
||||
|
||||
val isMainPod: Boolean
|
||||
|
||||
override val stableId: Long get() = device.identifier.hashCode().toLong()
|
||||
|
||||
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)?
|
||||
get() = { old, new ->
|
||||
if (new::class.isInstance(old)) new else null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.overview.cards.pods
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.lists.binding
|
||||
import eu.darken.capod.databinding.OverviewPodsSingleItemBinding
|
||||
import eu.darken.capod.pods.core.*
|
||||
import eu.darken.capod.wear.core.UserTime
|
||||
import java.time.Instant
|
||||
|
||||
class SinglePodsCardVH(parent: ViewGroup) :
|
||||
PodDeviceVH<SinglePodsCardVH.Item, OverviewPodsSingleItemBinding>(
|
||||
R.layout.overview_pods_single_item,
|
||||
parent
|
||||
) {
|
||||
|
||||
override val viewBinding = lazy { OverviewPodsSingleItemBinding.bind(itemView) }
|
||||
|
||||
override val onBindData = binding(payload = true) { item: Item ->
|
||||
userTime.text = item.userTime.toFormatted(context)
|
||||
|
||||
val device = item.device
|
||||
|
||||
name.apply {
|
||||
text = device.getLabel(context)
|
||||
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||
else setTypeface(null, Typeface.NORMAL)
|
||||
}
|
||||
|
||||
deviceIcon.setImageResource(device.iconRes)
|
||||
|
||||
lastSeen.text = device.lastSeenFormatted(item.now)
|
||||
|
||||
// Battery level
|
||||
device.apply {
|
||||
batteryLabel.text = getBatteryLevelHeadset(context)
|
||||
batteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent))
|
||||
}
|
||||
|
||||
// Charge state
|
||||
device.apply {
|
||||
if (this is HasChargeDetection) {
|
||||
chargingIcon.isInvisible = isHeadsetBeingCharged
|
||||
chargingLabel.isInvisible = isHeadsetBeingCharged
|
||||
} else {
|
||||
chargingIcon.isGone = true
|
||||
chargingLabel.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
// Has ear detection
|
||||
device.apply {
|
||||
if (this is HasEarDetection) {
|
||||
wearIcon.isInvisible = isBeingWorn
|
||||
wearLabel.isInvisible = isBeingWorn
|
||||
} else {
|
||||
wearIcon.isGone = true
|
||||
wearLabel.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
status.apply {
|
||||
val sb = StringBuilder()
|
||||
if (item.showDebug) {
|
||||
sb.append("--- Debug ---")
|
||||
sb.append("\n").append(device.rawDataHex)
|
||||
}
|
||||
text = sb
|
||||
isGone = !item.showDebug
|
||||
}
|
||||
}
|
||||
|
||||
data class Item(
|
||||
override val now: Instant,
|
||||
override val device: SinglePodDevice,
|
||||
override val showDebug: Boolean,
|
||||
override val isMainPod: Boolean,
|
||||
val userTime: UserTime,
|
||||
) : PodDeviceVH.Item
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.BuildConfigWrap
|
||||
import eu.darken.capod.common.uix.Fragment3
|
||||
import eu.darken.capod.common.viewbinding.viewBinding
|
||||
import eu.darken.capod.databinding.SettingsFragmentBinding
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsFragment : Fragment3(R.layout.settings_fragment) {
|
||||
|
||||
override val vm: SettingsFragmentVM by viewModels()
|
||||
override val ui: SettingsFragmentBinding by viewBinding()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
ui.appVersion.text = BuildConfigWrap.VERSION_DESCRIPTION_TINY
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package eu.darken.capod.wear.ui.settings
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.uix.ViewModel3
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsFragmentVM @Inject constructor(
|
||||
@Suppress("UNUSED_PARAMETER") handle: SavedStateHandle,
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
) : ViewModel3(dispatcherProvider = dispatcherProvider)
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:drawable="@mipmap/ic_launcher"
|
||||
android:gravity="center" />
|
||||
</layer-list>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".wear.ui.MainActivity">
|
||||
|
||||
<androidx.wear.widget.SwipeDismissFrameLayout
|
||||
android:id="@+id/swipe_wrapper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:navGraph="@navigation/nav_graph" />
|
||||
|
||||
</androidx.wear.widget.SwipeDismissFrameLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:listitem="@layout/overview_pods_dual_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/bluetooth_label"
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/overview_bluetooth_disabled_label"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bluetooth_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/bluetooth_description"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="@string/overview_bluetooth_disabled_description"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/bluetooth_label" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/user_time"
|
||||
style="@style/UserTimeStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="12:23" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/overview_nomaindevice_label"
|
||||
app:layout_constraintBottom_toTopOf="@id/body"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/body"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="@string/overview_nomaindevice_description"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,57 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/request_description"
|
||||
style="@style/TextAppearance.Material3.LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/permission_required_title"
|
||||
app:layout_constraintBottom_toTopOf="@+id/permission_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/permission_label"
|
||||
style="@style/TextAppearance.Material3.BodyLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/permission_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/request_description"
|
||||
tools:text="BLUETOOTH_CONNECT" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/permission_description"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/grant_action"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/permission_label"
|
||||
tools:text="Lorem Ipsum" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/grant_action"
|
||||
style="@style/Widget.Material3.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="@string/general_grant_permission_action"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/permission_description" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,289 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="8dp"
|
||||
tools:context=".wear.ui.MainActivity">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/user_time"
|
||||
style="@style/UserTimeStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="12:23" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/device_icon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/devic_airpods_gen1_both" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/name"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/status"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/device_icon"
|
||||
tools:text="Apple AirPods Pro" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/status"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center"
|
||||
android:lines="2"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintBottom_toTopOf="@id/barrier_top"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/name"
|
||||
app:lineHeight="14sp"
|
||||
tools:text="Music Active, Case Closed" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="status" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/pod_left_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
app:layout_constraintEnd_toStartOf="@id/pod_case_container"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/pod_right_container">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_left_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:contentDescription="@string/pods_dual_left_label"
|
||||
android:src="@drawable/devic_airpods_gen1_left"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pod_left_wear_icon"
|
||||
app:layout_constraintHorizontal_chainStyle="spread"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_left_wear_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:src="@drawable/ic_baseline_hearing_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pod_left_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pod_left_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/pod_left_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_left_battery_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginTop="1dp"
|
||||
android:src="@drawable/ic_baseline_battery_unknown_24"
|
||||
app:layout_constraintBottom_toTopOf="@id/pod_left_battery_label"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pod_left_charging_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_left_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_left_charging_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/ic_baseline_power_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pod_left_battery_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pod_left_battery_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/pod_left_battery_icon" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/pod_left_battery_label"
|
||||
style="@style/PodInfoItemText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:text="@string/general_value_not_available_label"
|
||||
app:layout_constraintEnd_toEndOf="@id/pod_left_charging_icon"
|
||||
app:layout_constraintStart_toStartOf="@id/pod_left_battery_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_left_battery_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_left_microphone_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:src="@drawable/ic_baseline_keyboard_voice_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.85"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_left_battery_label" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/pod_case_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pod_left_container"
|
||||
app:layout_constraintEnd_toStartOf="@id/pod_right_container"
|
||||
app:layout_constraintStart_toEndOf="@id/pod_left_container"
|
||||
app:layout_constraintTop_toTopOf="@id/pod_left_container"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_case_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/devic_airpods_gen1_case"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_case_battery_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginTop="1dp"
|
||||
android:src="@drawable/ic_baseline_battery_unknown_24"
|
||||
app:layout_constraintBottom_toTopOf="@id/pod_case_battery_label"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pod_case_charging_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_case_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_case_charging_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/ic_baseline_power_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pod_case_battery_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pod_case_battery_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/pod_case_battery_icon" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/pod_case_battery_label"
|
||||
style="@style/PodInfoItemText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:text="@string/general_value_not_available_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_case_battery_icon" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/pod_right_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pod_case_container"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier_top">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_right_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:contentDescription="@string/pods_dual_right_label"
|
||||
android:src="@drawable/devic_airpods_gen1_right"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toEndOf="@+id/pod_right_wear_icon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_right_wear_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:src="@drawable/ic_baseline_hearing_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pod_right_icon"
|
||||
app:layout_constraintEnd_toStartOf="@id/pod_right_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/pod_right_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_right_battery_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginTop="1dp"
|
||||
android:src="@drawable/ic_baseline_battery_unknown_24"
|
||||
app:layout_constraintBottom_toTopOf="@id/pod_right_battery_label"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pod_right_charging_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_right_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_right_charging_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/ic_baseline_power_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/pod_right_battery_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/pod_right_battery_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/pod_right_battery_icon" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/pod_right_battery_label"
|
||||
style="@style/PodInfoItemText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:text="@string/general_value_not_available_label"
|
||||
app:layout_constraintEnd_toEndOf="@id/pod_right_charging_icon"
|
||||
app:layout_constraintStart_toStartOf="@id/pod_right_battery_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_right_battery_icon" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pod_right_microphone_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:layout_marginHorizontal="2dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:src="@drawable/ic_baseline_keyboard_voice_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.15"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/pod_right_battery_label" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="top"
|
||||
app:constraint_referenced_ids="last_seen" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/last_seen"
|
||||
style="@style/TextAppearance.Material3.LabelSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:gravity="center"
|
||||
android:lines="2"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/barrier_bottom"
|
||||
tools:text="3s ago" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,158 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="8dp"
|
||||
tools:context=".wear.ui.MainActivity">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/user_time"
|
||||
style="@style/UserTimeStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="12:23" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/device_icon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toTopOf="@+id/name"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/devic_headphones_generic" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/name"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/status"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/device_icon"
|
||||
tools:text="Apple AirPods Max" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/status"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:gravity="center"
|
||||
android:lines="2"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintBottom_toTopOf="@id/barrier_top"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/name"
|
||||
tools:text="Music Active, Case Closed" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="status" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/pod_case_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/barrier_bottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier_top"
|
||||
app:layout_constraintVertical_bias="0.20">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/battery_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/ic_baseline_battery_unknown_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/battery_label"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/battery_label" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/battery_label"
|
||||
style="@style/PodInfoItemText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/general_value_not_available_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/battery_icon"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/charging_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/ic_baseline_power_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/charging_label"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/charging_label" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/charging_label"
|
||||
style="@style/PodInfoItemText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/pods_charging_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/charging_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/battery_label" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/wear_icon"
|
||||
style="@style/PodInfoItemIcon"
|
||||
android:src="@drawable/ic_baseline_hearing_24"
|
||||
app:layout_constraintBottom_toBottomOf="@id/wear_label"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/wear_label" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/wear_label"
|
||||
style="@style/PodInfoItemText"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="@string/headset_being_worn_label"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/wear_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/charging_label" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier_bottom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="top"
|
||||
app:constraint_referenced_ids="last_seen" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/last_seen"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:gravity="center"
|
||||
app:drawableStartCompat="@drawable/ic_baseline_watch_later_12"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="3s ago" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorPrimary"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingTop="8dp"
|
||||
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_baseline_settings_24"
|
||||
app:tint="?colorOnPrimary" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/app_version"
|
||||
style="@style/TextAppearance.Material3.LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:textColor="?colorOnPrimary"
|
||||
tools:text="v1.0.0" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background" />
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
app:startDestination="@id/overviewFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/overviewFragment"
|
||||
android:name="eu.darken.capod.wear.ui.overview.OverviewFragment"
|
||||
tools:layout="@layout/main_fragment">
|
||||
|
||||
<action
|
||||
android:id="@+id/action_overviewFragment_to_settingsFragment"
|
||||
app:destination="@id/settingsFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/settingsFragment"
|
||||
android:name="eu.darken.capod.wear.ui.settings.SettingsFragment"
|
||||
android:label="SettingsFragment"
|
||||
tools:layout="@layout/settings_fragment" />
|
||||
|
||||
</navigation>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
</resources>
|
||||