1 package com.android.healthconnect.controller.utils 2 3 import android.content.Context 4 import android.provider.DeviceConfig 5 import com.android.healthfitness.flags.Flags 6 import dagger.Module 7 import dagger.Provides 8 import dagger.hilt.InstallIn 9 import dagger.hilt.android.qualifiers.ApplicationContext 10 import dagger.hilt.components.SingletonComponent 11 import javax.inject.Singleton 12 13 interface FeatureUtils { isSessionTypesEnablednull14 fun isSessionTypesEnabled(): Boolean 15 16 fun isExerciseRouteReadAllEnabled(): Boolean 17 18 fun isEntryPointsEnabled(): Boolean 19 20 fun isNewAppPriorityEnabled(): Boolean 21 22 fun isNewInformationArchitectureEnabled(): Boolean 23 24 fun isBackgroundReadEnabled(): Boolean 25 26 fun isHistoryReadEnabled(): Boolean 27 28 fun isSkinTemperatureEnabled(): Boolean 29 30 fun isPlannedExerciseEnabled(): Boolean 31 32 fun isPersonalHealthRecordEnabled(): Boolean 33 } 34 35 class FeatureUtilsImpl(context: Context) : FeatureUtils, DeviceConfig.OnPropertiesChangedListener { 36 37 companion object { 38 private const val HEALTH_FITNESS_FLAGS_NAMESPACE = DeviceConfig.NAMESPACE_HEALTH_FITNESS 39 private const val PROPERTY_EXERCISE_ROUTE_ENABLED = "exercise_routes_enable" 40 private const val PROPERTY_EXERCISE_ROUTE_READ_ALL_ENABLED = 41 "exercise_routes_read_all_enable" 42 private const val PROPERTY_SESSIONS_TYPE_ENABLED = "session_types_enable" 43 private const val PROPERTY_ENTRY_POINTS_ENABLED = "entry_points_enable" 44 private const val PROPERTY_AGGREGATION_SOURCE_CONTROL_ENABLED = 45 "aggregation_source_controls_enable" 46 private const val PROPERTY_NEW_INFORMATION_ARCHITECTURE_ENABLED = 47 "new_information_architecture_enable" 48 private const val PROPERTY_PERSONAL_HEALTH_RECORD_ENABLED = "personal_health_record_enable" 49 } 50 51 private val lock = Any() 52 53 init { 54 DeviceConfig.addOnPropertiesChangedListener( 55 HEALTH_FITNESS_FLAGS_NAMESPACE, context.mainExecutor, this) 56 } 57 58 private var isSessionTypesEnabled = 59 DeviceConfig.getBoolean( 60 HEALTH_FITNESS_FLAGS_NAMESPACE, PROPERTY_SESSIONS_TYPE_ENABLED, true) 61 62 private var isExerciseRouteReadAllEnabled = true 63 64 private var isEntryPointsEnabled = 65 DeviceConfig.getBoolean(HEALTH_FITNESS_FLAGS_NAMESPACE, PROPERTY_ENTRY_POINTS_ENABLED, true) 66 67 private var isNewAppPriorityEnabled = true 68 69 private var isNewInformationArchitectureEnabled = 70 DeviceConfig.getBoolean( 71 HEALTH_FITNESS_FLAGS_NAMESPACE, PROPERTY_NEW_INFORMATION_ARCHITECTURE_ENABLED, false) 72 73 private var isPersonalHealthRecordEnabled = Flags.personalHealthRecord() 74 75 override fun isNewAppPriorityEnabled(): Boolean { 76 synchronized(lock) { 77 return isNewAppPriorityEnabled 78 } 79 } 80 81 override fun isNewInformationArchitectureEnabled(): Boolean { 82 synchronized(lock) { 83 return isNewInformationArchitectureEnabled 84 } 85 } 86 87 override fun isSessionTypesEnabled(): Boolean { 88 synchronized(lock) { 89 return isSessionTypesEnabled 90 } 91 } 92 93 override fun isExerciseRouteReadAllEnabled(): Boolean { 94 synchronized(lock) { 95 return isExerciseRouteReadAllEnabled 96 } 97 } 98 99 override fun isEntryPointsEnabled(): Boolean { 100 synchronized(lock) { 101 return isEntryPointsEnabled 102 } 103 } 104 105 override fun isBackgroundReadEnabled(): Boolean { 106 synchronized(lock) { 107 return true 108 } 109 } 110 111 override fun isHistoryReadEnabled(): Boolean { 112 synchronized(lock) { 113 return true 114 } 115 } 116 117 override fun isPlannedExerciseEnabled(): Boolean { 118 synchronized(lock) { 119 return true 120 } 121 } 122 123 override fun isSkinTemperatureEnabled(): Boolean { 124 synchronized(lock) { 125 return true 126 } 127 } 128 129 override fun isPersonalHealthRecordEnabled(): Boolean { 130 synchronized(lock) { 131 return isPersonalHealthRecordEnabled 132 } 133 } 134 135 override fun onPropertiesChanged(properties: DeviceConfig.Properties) { 136 synchronized(lock) { 137 if (!properties.namespace.equals(HEALTH_FITNESS_FLAGS_NAMESPACE)) { 138 return 139 } 140 141 for (name in properties.keyset) { 142 when (name) { 143 PROPERTY_EXERCISE_ROUTE_READ_ALL_ENABLED -> { 144 isExerciseRouteReadAllEnabled = 145 properties.getBoolean(PROPERTY_EXERCISE_ROUTE_READ_ALL_ENABLED, true) 146 } 147 PROPERTY_SESSIONS_TYPE_ENABLED -> 148 isSessionTypesEnabled = 149 properties.getBoolean(PROPERTY_SESSIONS_TYPE_ENABLED, true) 150 PROPERTY_ENTRY_POINTS_ENABLED -> 151 isEntryPointsEnabled = 152 properties.getBoolean(PROPERTY_ENTRY_POINTS_ENABLED, true) 153 PROPERTY_AGGREGATION_SOURCE_CONTROL_ENABLED -> isNewAppPriorityEnabled = true 154 PROPERTY_NEW_INFORMATION_ARCHITECTURE_ENABLED -> 155 isNewInformationArchitectureEnabled = 156 properties.getBoolean( 157 PROPERTY_NEW_INFORMATION_ARCHITECTURE_ENABLED, false) 158 PROPERTY_PERSONAL_HEALTH_RECORD_ENABLED -> 159 properties.getBoolean(PROPERTY_PERSONAL_HEALTH_RECORD_ENABLED, false) 160 } 161 } 162 } 163 } 164 } 165 166 @Module 167 @InstallIn(SingletonComponent::class) 168 class FeaturesModule { 169 @Provides 170 @Singleton providesFeatureUtilsnull171 fun providesFeatureUtils(@ApplicationContext context: Context): FeatureUtils { 172 return FeatureUtilsImpl(context) 173 } 174 } 175