1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.inputdevice.tutorial.data.repository 18 19 import android.content.Context 20 import androidx.datastore.core.DataStore 21 import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler 22 import androidx.datastore.preferences.core.Preferences 23 import androidx.datastore.preferences.core.edit 24 import androidx.datastore.preferences.core.emptyPreferences 25 import androidx.datastore.preferences.core.longPreferencesKey 26 import androidx.datastore.preferences.preferencesDataStore 27 import com.android.systemui.dagger.SysUISingleton 28 import com.android.systemui.dagger.qualifiers.Application 29 import com.android.systemui.dagger.qualifiers.Background 30 import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo 31 import java.time.Instant 32 import javax.inject.Inject 33 import kotlinx.coroutines.CoroutineScope 34 import kotlinx.coroutines.flow.first 35 import kotlinx.coroutines.flow.map 36 37 @SysUISingleton 38 class TutorialSchedulerRepository( 39 private val applicationContext: Context, 40 backgroundScope: CoroutineScope, 41 dataStoreName: String, 42 ) { 43 @Inject 44 constructor( 45 @Application applicationContext: Context, 46 @Background backgroundScope: CoroutineScope, 47 ) : this(applicationContext, backgroundScope, dataStoreName = DATASTORE_NAME) 48 49 private val Context.dataStore: DataStore<Preferences> by 50 preferencesDataStore( 51 name = dataStoreName, 52 corruptionHandler = 53 ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }), 54 scope = backgroundScope, 55 ) 56 57 suspend fun setScheduledTutorialLaunchTime(device: DeviceType, time: Instant) { 58 updateData(key = getLaunchedKey(device), value = time.epochSecond) 59 } 60 61 suspend fun isScheduledTutorialLaunched(deviceType: DeviceType): Boolean = 62 loadData()[deviceType]!!.isLaunched 63 64 suspend fun getScheduledTutorialLaunchTime(deviceType: DeviceType): Instant? = 65 loadData()[deviceType]!!.launchedTime 66 67 suspend fun setFirstConnectionTime(device: DeviceType, time: Instant) { 68 updateData(key = getConnectedKey(device), value = time.epochSecond) 69 } 70 71 suspend fun wasEverConnected(deviceType: DeviceType): Boolean = 72 loadData()[deviceType]!!.wasEverConnected 73 74 suspend fun getFirstConnectionTime(deviceType: DeviceType): Instant? = 75 loadData()[deviceType]!!.firstConnectionTime 76 77 suspend fun setNotifiedTime(device: DeviceType, time: Instant) { 78 updateData(key = getNotifiedKey(device), value = time.epochSecond) 79 } 80 81 suspend fun isNotified(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isNotified 82 83 suspend fun getNotifiedTime(deviceType: DeviceType): Instant? = 84 loadData()[deviceType]!!.notifiedTime 85 86 private suspend fun loadData(): Map<DeviceType, DeviceSchedulerInfo> { 87 return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first() 88 } 89 90 private suspend fun <T> updateData(key: Preferences.Key<T>, value: T) { 91 applicationContext.dataStore.edit { pref -> pref[key] = value } 92 } 93 94 private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> { 95 return mapOf( 96 DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD), 97 DeviceType.TOUCHPAD to getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD), 98 ) 99 } 100 101 private fun getDeviceSchedulerInfo(pref: Preferences, device: DeviceType): DeviceSchedulerInfo { 102 val launchedTime = pref[getLaunchedKey(device)] 103 val connectedTime = pref[getConnectedKey(device)] 104 val notifiedTime = pref[getNotifiedKey(device)] 105 return DeviceSchedulerInfo(launchedTime, connectedTime, notifiedTime) 106 } 107 108 private fun getLaunchedKey(device: DeviceType) = 109 longPreferencesKey(device.name + LAUNCHED_TIME_SUFFIX) 110 111 private fun getConnectedKey(device: DeviceType) = 112 longPreferencesKey(device.name + CONNECTED_TIME_SUFFIX) 113 114 private fun getNotifiedKey(device: DeviceType) = 115 longPreferencesKey(device.name + NOTIFIED_TIME_SUFFIX) 116 117 suspend fun clear() { 118 applicationContext.dataStore.edit { it.clear() } 119 } 120 121 companion object { 122 const val DATASTORE_NAME = "TutorialScheduler" 123 const val LAUNCHED_TIME_SUFFIX = "_LAUNCHED_TIME" 124 const val CONNECTED_TIME_SUFFIX = "_CONNECTED_TIME" 125 const val NOTIFIED_TIME_SUFFIX = "_NOTIFIED_TIME" 126 } 127 } 128 129 enum class DeviceType { 130 KEYBOARD, 131 TOUCHPAD, 132 } 133