[go: nahoru, domu]

blob: 89680ea5938d05eebaa13e3bb1c033a88e4b39b3 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.material.demos
import android.app.Activity
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.Composable
import androidx.compose.FrameManager
import androidx.compose.Model
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
import androidx.ui.core.setContent
import androidx.ui.graphics.Color
import androidx.ui.graphics.toArgb
import androidx.ui.material.ColorPalette
import androidx.ui.material.MaterialTheme
import androidx.ui.material.demos.MaterialSettingsActivity.SettingsFragment
import kotlin.random.Random
import kotlin.reflect.full.memberProperties
@Model
class CurrentColorPalette {
var colors: ColorPalette = ColorPalette()
}
/**
* Base [Activity] for material demos. Handles generating and editing the top level [MaterialTheme].
* Subclasses should override [materialContent] to emit their specific demos.
*/
abstract class MaterialDemoActivity : Activity() {
private val currentColors = CurrentColorPalette()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Ensure we are in a frame, as this is only normally initialized after the setContent call
FrameManager.ensureStarted()
currentColors.colors = getColorsFromSharedPreferences()
setContent {
MaterialTheme(currentColors.colors) {
materialContent()
}
}
}
override fun onResume() {
super.onResume()
// Update colors in case we changed something in settings activity
currentColors.colors = getColorsFromSharedPreferences()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menu?.add(Menu.NONE, SETTINGS, Menu.NONE, "Theme settings")
menu?.add(Menu.NONE, SHUFFLE, Menu.NONE, "Shuffle colors")
menu?.add(Menu.NONE, INVERT, Menu.NONE, "Invert color mapping")
menu?.add(Menu.NONE, RESET, Menu.NONE, "Reset theme to default")
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
SETTINGS -> startActivity(Intent(this, MaterialSettingsActivity::class.java))
SHUFFLE -> {
val colors = generateColorPalette(currentColors.colors)
colors.saveColors()
currentColors.colors = colors
}
INVERT -> {
// Flip all colors
val newPrimary = currentColors.colors.onPrimary
val newOnPrimary = currentColors.colors.primary
val newSecondary = currentColors.colors.onSecondary
val newOnSecondary = currentColors.colors.secondary
val colors = ColorPalette(
primary = newPrimary,
primaryVariant = currentColors.colors.primaryVariant,
secondary = newSecondary,
secondaryVariant = currentColors.colors.secondaryVariant,
background = currentColors.colors.background,
surface = currentColors.colors.surface,
error = currentColors.colors.error,
onPrimary = newOnPrimary,
onSecondary = newOnSecondary,
onBackground = currentColors.colors.onBackground,
onSurface = currentColors.colors.onSurface,
onError = currentColors.colors.onError
)
colors.saveColors()
currentColors.colors = colors
}
RESET -> {
val sharedPreferences = getDefaultSharedPreferences(this)
sharedPreferences.edit().clear().apply()
currentColors.colors = getColorsFromSharedPreferences()
}
}
return true
}
/**
* Returns [ColorPalette] from the values saved to [SharedPreferences]. If a given color is
* not present in the [SharedPreferences], its default value as defined in [ColorPalette]
* will be returned.
*/
private fun getColorsFromSharedPreferences(): ColorPalette {
val sharedPreferences = getDefaultSharedPreferences(this)
val function = ::ColorPalette
val parametersToSet = function.parameters.mapNotNull { parameter ->
val savedValue = sharedPreferences.getString(parameter.name, "")
if (savedValue.isNullOrBlank()) {
null
} else {
val parsedColor = Color(java.lang.Long.parseLong(savedValue, 16).toInt())
parameter to parsedColor
}
}.toMap()
if (parametersToSet.isEmpty()) return ColorPalette()
return ::ColorPalette.callBy(parametersToSet)
}
/**
* Persists the current [ColorPalette] to [SharedPreferences].
*/
private fun ColorPalette.saveColors() {
forEachColorProperty { name, color ->
getDefaultSharedPreferences(this@MaterialDemoActivity)
.edit()
.putString(name, Integer.toHexString(color.toArgb()))
.apply()
}
}
/**
* Generates random colors for [ColorPalette.primary], [ColorPalette.onPrimary],
* [ColorPalette.secondary] and [ColorPalette.onSecondary] as dark-on-light or light-on-dark
* pairs.
*/
private fun generateColorPalette(currentColors: ColorPalette): ColorPalette {
val (primary, onPrimary) = generateColorPair()
val (secondary, onSecondary) = generateColorPair()
return ColorPalette(
primary = primary,
primaryVariant = currentColors.primaryVariant,
secondary = secondary,
secondaryVariant = currentColors.secondaryVariant,
background = currentColors.background,
surface = currentColors.surface,
error = currentColors.error,
onPrimary = onPrimary,
onSecondary = onSecondary,
onBackground = currentColors.onBackground,
onSurface = currentColors.onSurface,
onError = currentColors.onError
)
}
/**
* Generate a random dark and light color from the palette, and returns either a dark-on-light
* or light-on-dark color pair.
*/
private fun generateColorPair(): Pair<Color, Color> {
val darkColor = Color(DARK_PALETTE_COLORS.random().toInt())
val lightColor = Color(LIGHT_PALETTE_COLORS.random().toInt())
val isMainColorLight = Random.nextBoolean()
return if (isMainColorLight) {
(lightColor to darkColor)
} else {
(darkColor to lightColor)
}
}
/**
* Override this function to return the composable hierarchy that should be displayed inside the
* customized [MaterialTheme].
*/
@Composable
abstract fun materialContent()
companion object {
private const val SETTINGS = 1
private const val SHUFFLE = 2
private const val INVERT = 3
private const val RESET = 4
// Colors taken from https://material.io/design/color -> 2014 Material Design color palettes
private val LIGHT_PALETTE_COLORS = listOf(
0xFFEF5350,
0xFFF44336,
0xFFE53935,
0xFFD32F2F,
0xFFC62828,
0xFFB71C1C,
0xFFFF5252,
0xFFFF1744,
0xFFD50000,
0xFFEC407A,
0xFFE91E63,
0xFFD81B60,
0xFFC2185B,
0xFFAD1457,
0xFF880E4F,
0xFFFF4081,
0xFFF50057,
0xFFC51162,
0xFFBA68C8,
0xFFAB47BC,
0xFF9C27B0,
0xFF8E24AA,
0xFF7B1FA2,
0xFF6A1B9A,
0xFF4A148C,
0xFFE040FB,
0xFFD500F9,
0xFFAA00FF,
0xFF9575CD,
0xFF7E57C2,
0xFF673AB7,
0xFF5E35B1,
0xFF512DA8,
0xFF4527A0,
0xFF311B92,
0xFF7C4DFF,
0xFF651FFF,
0xFF6200EA,
0xFF7986CB,
0xFF5C6BC0,
0xFF3F51B5,
0xFF3949AB,
0xFF303F9F,
0xFF283593,
0xFF1A237E,
0xFF536DFE,
0xFF3D5AFE,
0xFF304FFE,
0xFF1E88E5,
0xFF1976D2,
0xFF1565C0,
0xFF0D47A1,
0xFF448AFF,
0xFF2979FF,
0xFF2962FF,
0xFF0288D1,
0xFF0277BD,
0xFF01579B,
0xFF0091EA,
0xFF0097A7,
0xFF00838F,
0xFF006064,
0xFF009688,
0xFF00897B,
0xFF00796B,
0xFF00695C,
0xFF004D40,
0xFF43A047,
0xFF388E3C,
0xFF2E7D32,
0xFF1B5E20,
0xFF558B2F,
0xFF33691E,
0xFF827717,
0xFFE65100,
0xFFF4511E,
0xFFE64A19,
0xFFD84315,
0xFFBF360C,
0xFFFF3D00,
0xFFDD2C00,
0xFFA1887F,
0xFF8D6E63,
0xFF795548,
0xFF6D4C41,
0xFF5D4037,
0xFF4E342E,
0xFF3E2723,
0xFF757575,
0xFF616161,
0xFF424242,
0xFF212121,
0xFF78909C,
0xFF607D8B,
0xFF546E7A,
0xFF455A64,
0xFF37474F,
0xFF263238
)
private val DARK_PALETTE_COLORS = listOf(
0xFFFFCDD2,
0xFFEF9A9A,
0xFFE57373,
0xFFFF8A80,
0xFFF8BBD0,
0xFFF48FB1,
0xFFF06292,
0xFFFF80AB,
0xFFE1BEE7,
0xFFCE93D8,
0xFFEA80FC,
0xFFD1C4E9,
0xFFB39DDB,
0xFFB388FF,
0xFFC5CAE9,
0xFF9FA8DA,
0xFF8C9EFF,
0xFFBBDEFB,
0xFF90CAF9,
0xFF64B5F6,
0xFF42A5F5,
0xFF2196F3,
0xFF82B1FF,
0xFFB3E5FC,
0xFF81D4FA,
0xFF4FC3F7,
0xFF29B6F6,
0xFF03A9F4,
0xFF039BE5,
0xFF80D8FF,
0xFF40C4FF,
0xFF00B0FF,
0xFFB2EBF2,
0xFF80DEEA,
0xFF4DD0E1,
0xFF26C6DA,
0xFF00BCD4,
0xFF00ACC1,
0xFF84FFFF,
0xFF18FFFF,
0xFF00E5FF,
0xFF00B8D4,
0xFFB2DFDB,
0xFF80CBC4,
0xFF4DB6AC,
0xFF26A69A,
0xFFA7FFEB,
0xFF64FFDA,
0xFF1DE9B6,
0xFF00BFA5,
0xFFC8E6C9,
0xFFA5D6A7,
0xFF81C784,
0xFF66BB6A,
0xFF4CAF50,
0xFFB9F6CA,
0xFF69F0AE,
0xFF00E676,
0xFF00C853,
0xFFDCEDC8,
0xFFC5E1A5,
0xFFAED581,
0xFF9CCC65,
0xFF8BC34A,
0xFF7CB342,
0xFF689F38,
0xFFCCFF90,
0xFFB2FF59,
0xFF76FF03,
0xFF64DD17,
0xFFF0F4C3,
0xFFE6EE9C,
0xFFDCE775,
0xFFD4E157,
0xFFCDDC39,
0xFFC0CA33,
0xFFAFB42B,
0xFF9E9D24,
0xFFF4FF81,
0xFFEEFF41,
0xFFC6FF00,
0xFFAEEA00,
0xFFFFF9C4,
0xFFFFF59D,
0xFFFFF176,
0xFFFFEE58,
0xFFFFEB3B,
0xFFFDD835,
0xFFFBC02D,
0xFFF9A825,
0xFFF57F17,
0xFFFFFF8D,
0xFFFFFF00,
0xFFFFEA00,
0xFFFFD600,
0xFFFFECB3,
0xFFFFE082,
0xFFFFD54F,
0xFFFFCA28,
0xFFFFC107,
0xFFFFB300,
0xFFFFA000,
0xFFFF8F00,
0xFFFF6F00,
0xFFFFE57F,
0xFFFFD740,
0xFFFFC400,
0xFFFFAB00,
0xFFFFE0B2,
0xFFFFCC80,
0xFFFFB74D,
0xFFFFA726,
0xFFFF9800,
0xFFFB8C00,
0xFFF57C00,
0xFFEF6C00,
0xFFFFD180,
0xFFFFAB40,
0xFFFF9100,
0xFFFF6D00,
0xFFFFCCBC,
0xFFFFAB91,
0xFFFF8A65,
0xFFFF7043,
0xFFFF5722,
0xFFFF9E80,
0xFFFF6E40,
0xFFD7CCC8,
0xFFBCAAA4,
0xFFF5F5F5,
0xFFEEEEEE,
0xFFE0E0E0,
0xFFBDBDBD,
0xFF9E9E9E,
0xFFCFD8DC,
0xFFB0BEC5,
0xFF90A4AE,
0xFFFFFFFF
)
}
}
/**
* Shell [AppCompatActivity] around [SettingsFragment], as we need a FragmentActivity subclass
* to host the [SettingsFragment].
*/
class MaterialSettingsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportFragmentManager
.beginTransaction()
.replace(android.R.id.content, SettingsFragment())
.commit()
}
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val context = preferenceManager.context
val screen = preferenceManager.createPreferenceScreen(context)
// Create new ColorPalette to resolve defaults
ColorPalette().forEachColorProperty { name, color ->
val preference = EditTextPreference(context)
preference.key = name
preference.title = name
// set the default value to be the default for ColorPalette
preference.setDefaultValue(Integer.toHexString(color.toArgb()))
preference.summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
screen.addPreference(preference)
}
preferenceScreen = screen
}
}
}
/**
* Iterates over each color present in a given [ColorPalette].
*
* @param action the action to take on each property, where name is the name of the property,
* such as 'primary' for [ColorPalette.primary], and color is the resolved [Color] of the
* property.
*/
private fun ColorPalette.forEachColorProperty(action: (name: String, color: Color) -> Unit) {
ColorPalette::class.memberProperties.forEach { property ->
val name = property.name
val color = property.get(this) as Color
action(name, color)
}
}