From dbdfb3ac773a879a0a5caf2b195718cbe78da0f1 Mon Sep 17 00:00:00 2001 From: Andrew Mihin Date: Mon, 10 Sep 2018 21:36:00 +0300 Subject: [PATCH 1/5] Add controls to set custom size. Add new preference storage model --- .../main/java/com/devindi/wallpaper/App.kt | 4 + .../misc/AfterTextChangedListener.kt | 18 +++ .../misc/FixedLifecycleController.kt | 19 ++++ .../devindi/wallpaper/misc/NonNullLiveData.kt | 19 ++++ .../misc/SimpleSeekBarChangeListener.kt | 28 +++++ .../wallpaper/settings/SettingsAdapter.kt | 25 +++++ .../wallpaper/settings/SettingsController.kt | 31 +++++- .../wallpaper/settings/model/IntField.kt | 19 ++++ .../wallpaper/settings/model/SettingsField.kt | 12 ++ .../settings/model/SettingsManager.kt | 25 +++++ .../wallpaper/settings/model/StringField.kt | 18 +++ .../wallpaper/settings/size/SizeViewHolder.kt | 25 +++++ .../settings/size/edit/EditSizeController.kt | 93 ++++++++++++++++ .../settings/size/edit/EditSizeViewModel.kt | 26 +++++ app/src/main/res/layout/settings_screen.xml | 5 +- .../main/res/layout/settings_simple_item.xml | 27 +++++ .../main/res/layout/settings_size_edit.xml | 105 ++++++++++++++++++ app/src/main/res/values/strings.xml | 3 + 18 files changed, 499 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/devindi/wallpaper/misc/AfterTextChangedListener.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/misc/FixedLifecycleController.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/misc/NonNullLiveData.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/misc/SimpleSeekBarChangeListener.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/SettingsAdapter.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/model/IntField.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/model/SettingsField.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/model/SettingsManager.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/model/StringField.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/size/SizeViewHolder.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeController.kt create mode 100644 app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeViewModel.kt create mode 100644 app/src/main/res/layout/settings_simple_item.xml create mode 100644 app/src/main/res/layout/settings_size_edit.xml diff --git a/app/src/main/java/com/devindi/wallpaper/App.kt b/app/src/main/java/com/devindi/wallpaper/App.kt index 41e4777..bc469c1 100644 --- a/app/src/main/java/com/devindi/wallpaper/App.kt +++ b/app/src/main/java/com/devindi/wallpaper/App.kt @@ -16,6 +16,8 @@ import com.devindi.wallpaper.model.search.searchModule import com.devindi.wallpaper.model.storage.KeyValueStorage import com.devindi.wallpaper.model.storage.MapCacheStrategy import com.devindi.wallpaper.model.storage.SharedPreferencesStorage +import com.devindi.wallpaper.settings.model.SettingsManager +import com.devindi.wallpaper.settings.size.edit.EditSizeViewModel import com.devindi.wallpaper.source.MapSourceViewModel import com.devindi.wallpaper.splash.SplashViewModel import com.squareup.picasso.Picasso @@ -44,9 +46,11 @@ class App : Application() { single { ConfigManager() } single { DependencyStrategy(get(), get()) } single { Configuration.getInstance() } + single { SettingsManager(PreferenceManager.getDefaultSharedPreferences(get())) } viewModel { SplashViewModel(get(), get()) } viewModel { HomeViewModel(get(), get(), get(), get()) } viewModel { MapSourceViewModel(get(), get()) } + viewModel { EditSizeViewModel(get()) } } override fun onCreate() { diff --git a/app/src/main/java/com/devindi/wallpaper/misc/AfterTextChangedListener.kt b/app/src/main/java/com/devindi/wallpaper/misc/AfterTextChangedListener.kt new file mode 100644 index 0000000..64a61cd --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/misc/AfterTextChangedListener.kt @@ -0,0 +1,18 @@ +package com.devindi.wallpaper.misc + +import android.text.Editable +import android.text.TextWatcher + +class AfterTextChangedListener(val observer: (text: String) -> Unit) : TextWatcher { + override fun afterTextChanged(s: Editable?) { + observer(s.toString()) + } + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + //do nothing + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + //do nothing + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/misc/FixedLifecycleController.kt b/app/src/main/java/com/devindi/wallpaper/misc/FixedLifecycleController.kt new file mode 100644 index 0000000..0788f89 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/misc/FixedLifecycleController.kt @@ -0,0 +1,19 @@ +package com.devindi.wallpaper.misc + +import android.arch.lifecycle.LifecycleRegistry +import android.arch.lifecycle.LifecycleRegistryOwner +import android.os.Bundle +import com.bluelinelabs.conductor.Controller +import com.bluelinelabs.conductor.archlifecycle.ControllerLifecycleRegistryOwner + +abstract class FixedLifecycleController : Controller, LifecycleRegistryOwner { + + constructor(): super() {} + constructor(args: Bundle): super(args) {} + + private val lifecycleRegistryOwner = ControllerLifecycleRegistryOwner(this) + + override fun getLifecycle(): LifecycleRegistry { + return lifecycleRegistryOwner.lifecycle + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/misc/NonNullLiveData.kt b/app/src/main/java/com/devindi/wallpaper/misc/NonNullLiveData.kt new file mode 100644 index 0000000..261f028 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/misc/NonNullLiveData.kt @@ -0,0 +1,19 @@ +package com.devindi.wallpaper.misc + +import android.arch.lifecycle.LifecycleOwner +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MediatorLiveData +import android.arch.lifecycle.Observer + +class NonNullMediatorLiveData : MediatorLiveData() + +fun LiveData.nonNull(): NonNullMediatorLiveData { + val mediator: NonNullMediatorLiveData = NonNullMediatorLiveData() + mediator.addSource(this) { it?.let { mediator.value = it } } + return mediator +} +fun NonNullMediatorLiveData.observe(owner: LifecycleOwner, observer: (t: T) -> Unit) { + this.observe(owner, Observer { + it?.let(observer) + }) +} diff --git a/app/src/main/java/com/devindi/wallpaper/misc/SimpleSeekBarChangeListener.kt b/app/src/main/java/com/devindi/wallpaper/misc/SimpleSeekBarChangeListener.kt new file mode 100644 index 0000000..ffa37ef --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/misc/SimpleSeekBarChangeListener.kt @@ -0,0 +1,28 @@ +package com.devindi.wallpaper.misc + +import android.widget.SeekBar + +/** + * Simple [SeekBar.OnSeekBarChangeListener] implementation with progress observer only. + * + * Usage sample: + * seekBar.setOnSeekBarChangeListener(SimpleSeekBarChangeListener { value, fromUser -> + * if (fromUser) { + * updateValue(value) + * } + * }) + * + */ +class SimpleSeekBarChangeListener(private val observer: (progress: Int, fromUser: Boolean) -> Unit) : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + observer(progress, fromUser) + } + + override fun onStartTrackingTouch(seekBar: SeekBar?) { + //do nothing + } + + override fun onStopTrackingTouch(seekBar: SeekBar?) { + //do nothing + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/SettingsAdapter.kt b/app/src/main/java/com/devindi/wallpaper/settings/SettingsAdapter.kt new file mode 100644 index 0000000..ef93712 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/SettingsAdapter.kt @@ -0,0 +1,25 @@ +package com.devindi.wallpaper.settings + +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup +import com.devindi.wallpaper.R +import com.devindi.wallpaper.settings.model.IntField +import com.devindi.wallpaper.settings.size.SizeViewHolder +import com.devindi.wallpaper.source.OnItemClickListener + +class SettingsAdapter(private val items: List, private val clickListener: OnItemClickListener) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.settings_simple_item, parent, false) + return SizeViewHolder(view, clickListener) + } + + override fun getItemCount(): Int { + return items.size + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + (holder as SizeViewHolder).bindSettingsField(items[position]) + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/SettingsController.kt b/app/src/main/java/com/devindi/wallpaper/settings/SettingsController.kt index 88df49c..4c957de 100644 --- a/app/src/main/java/com/devindi/wallpaper/settings/SettingsController.kt +++ b/app/src/main/java/com/devindi/wallpaper/settings/SettingsController.kt @@ -1,19 +1,35 @@ package com.devindi.wallpaper.settings +import android.os.Bundle import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.Toolbar import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.bluelinelabs.conductor.RouterTransaction import com.devindi.wallpaper.R import com.devindi.wallpaper.misc.BaseController import com.devindi.wallpaper.misc.ReportManager import com.devindi.wallpaper.misc.inject import com.devindi.wallpaper.model.analytics.ScreenEvent +import com.devindi.wallpaper.settings.model.DIMENSION_HEIGHT +import com.devindi.wallpaper.settings.model.DIMENSION_WIDTH +import com.devindi.wallpaper.settings.model.SettingsManager +import com.devindi.wallpaper.settings.size.edit.DIMENSION_KEY +import com.devindi.wallpaper.settings.size.edit.EditSizeController +import com.devindi.wallpaper.settings.size.edit.TITLE_KEY +import com.devindi.wallpaper.source.OnItemClickListener +import kotlinx.android.synthetic.main.settings_screen.view.* -class SettingsController : BaseController() { +class SettingsController : BaseController(), OnItemClickListener { private val reportManager: ReportManager by inject() + private val settingsManager: SettingsManager by inject() + + private val items = listOf( + settingsManager.getIntField(DIMENSION_HEIGHT), + settingsManager.getIntField(DIMENSION_WIDTH)) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { val view = inflater.inflate(R.layout.settings_screen, container, false) @@ -25,6 +41,10 @@ class SettingsController : BaseController() { } toolbar.setNavigationOnClickListener { router.popCurrentController() } + val listView = view.settings_list + listView.layoutManager = LinearLayoutManager(activity) + listView.adapter = SettingsAdapter(items, this) + return view } @@ -32,4 +52,11 @@ class SettingsController : BaseController() { super.onAttach(view) reportManager.reportEvent(ScreenEvent("settings")) } -} \ No newline at end of file + + override fun onItemClick(position: Int, view: View) { + val args = Bundle() + args.putString(DIMENSION_KEY, items[position].key) + args.putInt(TITLE_KEY, items[position].titleId) + router.pushController(RouterTransaction.with(EditSizeController(args))) + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/model/IntField.kt b/app/src/main/java/com/devindi/wallpaper/settings/model/IntField.kt new file mode 100644 index 0000000..b245c45 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/model/IntField.kt @@ -0,0 +1,19 @@ +package com.devindi.wallpaper.settings.model + +import android.content.SharedPreferences +import android.support.annotation.StringRes + +class IntField(private val storage: SharedPreferences, @StringRes public val titleId: Int, override val key: String): SettingsField { + + override fun get(): Int { + return get(0) + } + + override fun get(fallback: Int): Int { + return storage.getInt(key, fallback) + } + + override fun set(value: Int) { + storage.edit().putInt(key, value).apply() + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/model/SettingsField.kt b/app/src/main/java/com/devindi/wallpaper/settings/model/SettingsField.kt new file mode 100644 index 0000000..f606af5 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/model/SettingsField.kt @@ -0,0 +1,12 @@ +package com.devindi.wallpaper.settings.model + +interface SettingsField { + + val key: String + + fun get(): T + + fun get(fallback: T): T + + fun set(value: T) +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/model/SettingsManager.kt b/app/src/main/java/com/devindi/wallpaper/settings/model/SettingsManager.kt new file mode 100644 index 0000000..304af4c --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/model/SettingsManager.kt @@ -0,0 +1,25 @@ +package com.devindi.wallpaper.settings.model + +import android.content.SharedPreferences +import com.devindi.wallpaper.R + +const val DIMENSION_HEIGHT = "wallpaper_height" +const val DIMENSION_WIDTH = "wallpaper_width" + +class SettingsManager(storage: SharedPreferences) { + + private val map = HashMap>() + + init { + addField(IntField(storage, R.string.settings_item_height, DIMENSION_HEIGHT)) + addField(IntField(storage, R.string.settings_item_width, DIMENSION_WIDTH)) + } + + fun getIntField(key: String) : IntField { + return map[key] as IntField + } + + private fun addField(field : SettingsField<*>) { + map[field.key] = field + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/model/StringField.kt b/app/src/main/java/com/devindi/wallpaper/settings/model/StringField.kt new file mode 100644 index 0000000..e6a7082 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/model/StringField.kt @@ -0,0 +1,18 @@ +package com.devindi.wallpaper.settings.model + +import android.content.SharedPreferences + +class StringField(private val storage: SharedPreferences, override val key: String) : SettingsField { + + override fun get(): String { + return get("") + } + + override fun get(fallback: String): String { + return storage.getString(key, fallback) + } + + override fun set(value: String) { + storage.edit().putString(key, value).apply() + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/size/SizeViewHolder.kt b/app/src/main/java/com/devindi/wallpaper/settings/size/SizeViewHolder.kt new file mode 100644 index 0000000..542379d --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/size/SizeViewHolder.kt @@ -0,0 +1,25 @@ +package com.devindi.wallpaper.settings.size + +import android.support.v7.widget.RecyclerView +import android.view.View +import android.widget.TextView +import com.devindi.wallpaper.settings.model.IntField +import com.devindi.wallpaper.source.OnItemClickListener +import kotlinx.android.synthetic.main.settings_simple_item.view.* + +class SizeViewHolder(view: View, clickListener: OnItemClickListener) : RecyclerView.ViewHolder(view) { + + private val titleLabel: TextView = view.settings_title + private val valueLabel: TextView = view.settings_value + + init { + view.setOnClickListener{ + clickListener.onItemClick(adapterPosition, it) + } + } + + fun bindSettingsField(value: IntField) { + titleLabel.setText(value.titleId) + valueLabel.text = "${value.get(1000)} px" + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeController.kt b/app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeController.kt new file mode 100644 index 0000000..1802678 --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeController.kt @@ -0,0 +1,93 @@ +package com.devindi.wallpaper.settings.size.edit + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.SeekBar +import android.widget.TextView +import com.devindi.wallpaper.R +import com.devindi.wallpaper.misc.* +import com.devindi.wallpaper.model.DeviceInfo +import com.devindi.wallpaper.settings.model.DIMENSION_HEIGHT +import com.devindi.wallpaper.settings.model.DIMENSION_WIDTH +import kotlinx.android.synthetic.main.settings_size_edit.view.* + +const val TITLE_KEY = "title" +const val DIMENSION_KEY = "dimen" + +class EditSizeController(args: Bundle) : FixedLifecycleController(args) { + + private val viewModel: EditSizeViewModel by viewModel() + private val deviceInfo: DeviceInfo by inject() + private val screenSize = when (args.getString(DIMENSION_KEY)) { + DIMENSION_HEIGHT -> deviceInfo.screenHeight() + DIMENSION_WIDTH -> deviceInfo.screenWidth() + else -> throw IllegalArgumentException("Invalid dimension") + } + + private lateinit var textInput: EditText + private lateinit var seekInput: SeekBar + private lateinit var sizePreview: TextView + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.settings_size_edit, container, false) + } + + override fun onAttach(view: View) { + view.size_edit_title.setText(args.getInt(TITLE_KEY)) + viewModel.loadSize(args.getString(DIMENSION_KEY)) + + seekInput = view.seek_input + textInput = view.input + sizePreview = view.alter_size_label + + seekInput.setOnSeekBarChangeListener(SimpleSeekBarChangeListener { value, fromUser -> + if (fromUser) { + setSizeFromSeek((value + screenSize*0.5).toInt()) + } + }) + + textInput.addTextChangedListener(AfterTextChangedListener { text -> + if (text.isNotEmpty()) { + setSizeFromInput(text.toInt()) + } else { + setSizeFromInput(0) + } + }) + + seekInput.max = ((screenSize * 10) - screenSize * 0.5).toInt() + + view.ok.setOnClickListener { + viewModel.setSize(textInput.text.toString().toInt()) + router.popCurrentController() + } + view.cancel.setOnClickListener { + router.popCurrentController() + } + + viewModel.wallpaperSize().nonNull().observe(this) { size -> initViews(size) } + } + + private fun setSizeFromSeek(size: Int) { + textInput.setText(size.toString()) + updatePreview(size) + } + + private fun setSizeFromInput(size: Int) { + seekInput.progress = (size - screenSize * 0.5).toInt() + updatePreview(size) + } + + private fun initViews(size: Int) { + seekInput.progress = (size - screenSize * 0.5).toInt() + textInput.setText(size.toString()) + updatePreview(size) + } + + private fun updatePreview(size: Int) { + val multiplier = size / screenSize.toDouble() + sizePreview.text = String.format("(screen * %.2f)", multiplier) + } +} diff --git a/app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeViewModel.kt b/app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeViewModel.kt new file mode 100644 index 0000000..12915fd --- /dev/null +++ b/app/src/main/java/com/devindi/wallpaper/settings/size/edit/EditSizeViewModel.kt @@ -0,0 +1,26 @@ +package com.devindi.wallpaper.settings.size.edit + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData +import android.arch.lifecycle.ViewModel +import com.devindi.wallpaper.settings.model.IntField +import com.devindi.wallpaper.settings.model.SettingsManager + +class EditSizeViewModel(private val settingsManager: SettingsManager) : ViewModel() { + + private lateinit var field: IntField + private val wallpaperSize = MutableLiveData() + + fun loadSize(key: String) { + field = settingsManager.getIntField(key) + wallpaperSize.value = field.get() + } + + fun setSize(value: Int) { + field.set(value) + } + + fun wallpaperSize() : LiveData { + return wallpaperSize + } +} diff --git a/app/src/main/res/layout/settings_screen.xml b/app/src/main/res/layout/settings_screen.xml index d6e14d9..6c1b323 100644 --- a/app/src/main/res/layout/settings_screen.xml +++ b/app/src/main/res/layout/settings_screen.xml @@ -12,6 +12,9 @@ android:id="@+id/toolbar" app:title="Settings"/> - + \ No newline at end of file diff --git a/app/src/main/res/layout/settings_simple_item.xml b/app/src/main/res/layout/settings_simple_item.xml new file mode 100644 index 0000000..ca21b27 --- /dev/null +++ b/app/src/main/res/layout/settings_simple_item.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/app/src/main/res/layout/settings_size_edit.xml b/app/src/main/res/layout/settings_size_edit.xml new file mode 100644 index 0000000..1edf2c4 --- /dev/null +++ b/app/src/main/res/layout/settings_size_edit.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + +