Skip to content

Commit

Permalink
Merge pull request #16 from devindi/custom_size
Browse files Browse the repository at this point in the history
Add controls to set custom size. Add new preference storage model
  • Loading branch information
devindi committed Oct 16, 2018
2 parents fdd63e2 + 7b9415c commit eb40a23
Show file tree
Hide file tree
Showing 20 changed files with 582 additions and 9 deletions.
9 changes: 8 additions & 1 deletion app/src/main/java/com/devindi/wallpaper/App.kt
Expand Up @@ -9,13 +9,17 @@ import com.devindi.wallpaper.misc.ReportManager
import com.devindi.wallpaper.misc.createPermissionManager
import com.devindi.wallpaper.model.AndroidInfo
import com.devindi.wallpaper.model.DeviceInfo
import com.devindi.wallpaper.model.DisplayInfo
import com.devindi.wallpaper.model.SettingsRepo
import com.devindi.wallpaper.model.config.ConfigManager
import com.devindi.wallpaper.model.map.TileRequestHandler
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.SettingsViewModel
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
Expand All @@ -39,14 +43,17 @@ class App : Application() {
single { SettingsRepo(get(), get()) }
single { createPermissionManager() }
single { FabricReportManager() as ReportManager }
single { DeviceInfo(androidApplication()) as AndroidInfo }
single { DeviceInfo(androidApplication()) } bind AndroidInfo::class bind DisplayInfo::class
single { MapCacheStrategy(androidApplication(), get()) }
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()) }
viewModel { SettingsViewModel(get()) }
}

override fun onCreate() {
Expand Down
@@ -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
}
}
19 changes: 19 additions & 0 deletions 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<T> : MediatorLiveData<T>()

fun <T> LiveData<T>.nonNull(): NonNullMediatorLiveData<T> {
val mediator: NonNullMediatorLiveData<T> = NonNullMediatorLiveData()
mediator.addSource(this) { it?.let { mediator.value = it } }
return mediator
}
fun <T> NonNullMediatorLiveData<T>.observe(owner: LifecycleOwner, observer: (t: T) -> Unit) {
this.observe(owner, Observer {
it?.let(observer)
})
}
@@ -0,0 +1,30 @@
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
}
}
Expand Up @@ -10,6 +10,7 @@ import timber.log.Timber
private const val HALF_CIRCLE_DEGREES = 180
private const val CIRCLE_DEGREES = 360

@Deprecated("This class is designed by old me. Use MapImageGenerator instead")
class MapAreaManager(
private val tileProvider: SyncMapTileProvider,
private val sourceFactory: TileSourceFactory
Expand Down
@@ -0,0 +1,13 @@
package com.devindi.wallpaper.model.map

import android.graphics.Bitmap

/**
* Generates bitmap by params (source, center coordinates, zoom, size)
*/
class MapImageGenerator() {

fun generate(): Bitmap {
throw NotImplementedError()
}
}
@@ -0,0 +1,34 @@
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 clickListener: OnItemClickListener
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

var items: List<IntField> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}

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])
}
}
@@ -1,35 +1,65 @@
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.bluelinelabs.conductor.changehandler.FadeChangeHandler
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.misc.observe
import com.devindi.wallpaper.model.analytics.ScreenEvent
import com.devindi.wallpaper.settings.model.IntField
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.*
import org.koin.android.viewmodel.ext.android.viewModel

class SettingsController : BaseController() {
class SettingsController : BaseController(), OnItemClickListener {

private val reportManager: ReportManager by inject()
private val viewModel: SettingsViewModel by viewModel()
private val adapter = SettingsAdapter(this)

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View {
val view = inflater.inflate(R.layout.settings_screen, container, false)
return inflater.inflate(R.layout.settings_screen, container, false)
}

override fun onAttach(view: View) {
super.onAttach(view)
val toolbar = view.findViewById<Toolbar>(R.id.toolbar)
(activity as AppCompatActivity).let {
it.setSupportActionBar(toolbar)
it.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
toolbar.setNavigationOnClickListener { router.popCurrentController() }

return view
val listView = view.settings_list
listView.layoutManager = LinearLayoutManager(activity)
listView.adapter = adapter
reportManager.reportEvent(ScreenEvent("settings"))

viewModel.settings().observe(this) {
settings -> adapter.items = settings.map { it as IntField }
}
}

override fun onAttach(view: View) {
super.onAttach(view)
reportManager.reportEvent(ScreenEvent("settings"))
override fun onItemClick(position: Int, view: View) {
val args = Bundle()
args.putString(DIMENSION_KEY, adapter.items[position].key)
args.putInt(TITLE_KEY, adapter.items[position].titleId)
router.pushController(
RouterTransaction.with(EditSizeController(args))
.pushChangeHandler(FadeChangeHandler(false))
.popChangeHandler(FadeChangeHandler())
)
}
}
}
@@ -0,0 +1,36 @@
package com.devindi.wallpaper.settings

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModel
import com.devindi.wallpaper.misc.NonNullMediatorLiveData
import com.devindi.wallpaper.misc.nonNull
import com.devindi.wallpaper.settings.model.DIMENSION_HEIGHT
import com.devindi.wallpaper.settings.model.DIMENSION_WIDTH
import com.devindi.wallpaper.settings.model.SettingsField
import com.devindi.wallpaper.settings.model.SettingsManager

class SettingsViewModel(private val settingsManager: SettingsManager) : ViewModel() {

private val items = MutableLiveData<List<SettingsField<*>>>()
private val settingsObserver = Observer<String> {
notifySettingsChange()
}

init {
notifySettingsChange()
settingsManager.fieldChange().observeForever(settingsObserver)
}

fun settings(): NonNullMediatorLiveData<List<SettingsField<*>>> = items.nonNull()

override fun onCleared() {
settingsManager.fieldChange().removeObserver(settingsObserver)
}

private fun notifySettingsChange() {
items.value = listOf(
settingsManager.getIntField(DIMENSION_HEIGHT),
settingsManager.getIntField(DIMENSION_WIDTH))
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/com/devindi/wallpaper/settings/model/IntField.kt
@@ -0,0 +1,23 @@
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<Int> {

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()
}
}
@@ -0,0 +1,12 @@
package com.devindi.wallpaper.settings.model

interface SettingsField<T> {

val key: String

fun get(): T

fun get(fallback: T): T

fun set(value: T)
}
@@ -0,0 +1,34 @@
package com.devindi.wallpaper.settings.model

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
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<String, SettingsField<*>>()
private val fieldLive = MutableLiveData<String>()
private val changeListener = SharedPreferences.OnSharedPreferenceChangeListener {
_, key -> fieldLive.value = key
}

init {
addField(IntField(storage, R.string.settings_item_height, DIMENSION_HEIGHT))
addField(IntField(storage, R.string.settings_item_width, DIMENSION_WIDTH))
storage.registerOnSharedPreferenceChangeListener(changeListener)
}

fun getIntField(key: String): IntField {
return map[key] as IntField
}

fun fieldChange(): LiveData<String> = fieldLive

private fun addField(field: SettingsField<*>) {
map[field.key] = field
}
}
@@ -0,0 +1,21 @@
package com.devindi.wallpaper.settings.model

import android.content.SharedPreferences

class StringField(
private val storage: SharedPreferences,
override val key: String
) : SettingsField<String> {

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()
}
}
@@ -0,0 +1,28 @@
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(0)} px"
}
}

0 comments on commit eb40a23

Please sign in to comment.