1 /* <lambda>null2 * Copyright 2023 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 androidx.core.performance.play.services 18 19 import android.content.Context 20 import android.util.Log 21 import androidx.core.performance.DefaultDevicePerformance 22 import androidx.core.performance.DevicePerformance 23 import androidx.datastore.core.DataStore 24 import androidx.datastore.preferences.core.PreferenceDataStoreFactory 25 import androidx.datastore.preferences.core.Preferences 26 import androidx.datastore.preferences.core.edit 27 import androidx.datastore.preferences.core.intPreferencesKey 28 import androidx.datastore.preferences.preferencesDataStoreFile 29 import com.google.android.gms.common.api.ApiException 30 import com.google.android.gms.deviceperformance.DevicePerformanceClient 31 import kotlin.math.max 32 import kotlinx.coroutines.flow.Flow 33 import kotlinx.coroutines.flow.first 34 import kotlinx.coroutines.flow.map 35 import kotlinx.coroutines.launch 36 import kotlinx.coroutines.runBlocking 37 38 private const val MPC_PREFERENCE_KEY = "mpc_value" 39 40 /** 41 * A DevicePerformance that uses Google Play Services to retrieve media performance class data. 42 * 43 * @param context The application context value to use. 44 */ 45 public class PlayServicesDevicePerformance 46 internal constructor( 47 private val context: Context, 48 client: DevicePerformanceClient, 49 private val performanceStore: DataStore<Preferences> 50 ) : DevicePerformance { 51 private val tag = "PlayServicesDevicePerformance" 52 53 private val defaultMpc = DefaultDevicePerformance() 54 private val mpcKey = intPreferencesKey(MPC_PREFERENCE_KEY) 55 56 override val mediaPerformanceClass: Int 57 get() = lazyMpc.value 58 59 private val lazyMpc = lazy { 60 runBlocking { 61 val storedMpc = getPerformanceClass().first() 62 Log.v(tag, "Stored mpc is $storedMpc") 63 Log.v(tag, "Default mpc is ${defaultMpc.mediaPerformanceClass}") 64 val returnedMpc = max(storedMpc ?: 0, defaultMpc.mediaPerformanceClass) 65 Log.v(tag, "Mpc value used $returnedMpc") 66 return@runBlocking returnedMpc 67 } 68 } 69 70 init { 71 Log.v( 72 tag, 73 "Getting mediaPerformanceClass from " + 74 "com.google.android.gms.deviceperformance.DevicePerformanceClient" 75 ) 76 updatePerformanceStore(client) 77 } 78 79 /** 80 * A DevicePerformance that uses Google Play Services to retrieve media performance class data. 81 * 82 * @param context The application context value to use. 83 */ 84 public constructor( 85 context: Context 86 ) : this( 87 context, 88 com.google.android.gms.deviceperformance.DevicePerformance.getClient(context), 89 PreferenceDataStoreFactory.create( 90 produceFile = { context.preferencesDataStoreFile("media_performance_class") } 91 ) 92 ) 93 94 private fun getPerformanceClass(): Flow<Int?> { 95 return performanceStore.data.map { values -> 96 // No type safety. 97 values[mpcKey] 98 } 99 } 100 101 private suspend fun savePerformanceClass(value: Int) { 102 performanceStore.edit { values -> values[mpcKey] = value } 103 } 104 105 private fun updatePerformanceStore(client: DevicePerformanceClient) { 106 client 107 .mediaPerformanceClass() 108 .addOnSuccessListener { result -> 109 runBlocking { 110 Log.v(tag, "Got mediaPerformanceClass $result") 111 val storedVal = max(result, defaultMpc.mediaPerformanceClass) 112 launch { 113 savePerformanceClass(storedVal) 114 Log.v(tag, "Saved mediaPerformanceClass $storedVal") 115 } 116 } 117 } 118 .addOnFailureListener { e: Exception -> 119 if (e is ApiException) { 120 Log.e(tag, "Error saving mediaPerformanceClass", e) 121 } else if (e is IllegalStateException) { 122 Log.e(tag, "Error saving mediaPerformanceClass", e) 123 } 124 } 125 } 126 } 127