mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-22 18:51:49 +01:00
Custom color filter for reader (#434)
* [WIP] Custom color filter for reader * Improvements * temp image to prevent build error * Shift all the bits * Some improvements. Removed DiscreteSeekBar * Improvements * API 16 + fixes * Reduced lag. Fixed brightness value being reset to 0 * Small fixes
This commit is contained in:
parent
58a2f7a874
commit
8be67a4431
@ -165,7 +165,6 @@ dependencies {
|
||||
compile 'com.afollestad.material-dialogs:core:0.8.6.2'
|
||||
compile 'net.xpece.android:support-preference:0.8.1'
|
||||
compile 'me.zhanghai.android.systemuihelper:library:1.0.0'
|
||||
compile 'org.adw.library:discrete-seekbar:1.0.1'
|
||||
compile 'de.hdodenhof:circleimageview:2.1.0'
|
||||
|
||||
// Tests
|
||||
|
@ -26,6 +26,10 @@ class PreferenceKeys(context: Context) {
|
||||
|
||||
val customBrightnessValue = context.getString(R.string.pref_custom_brightness_value_key)
|
||||
|
||||
val colorFilter = context.getString(R.string.pref_color_filter_key)
|
||||
|
||||
val colorFilterValue = context.getString(R.string.pref_color_filter_value_key)
|
||||
|
||||
val defaultViewer = context.getString(R.string.pref_default_viewer_key)
|
||||
|
||||
val imageScaleType = context.getString(R.string.pref_image_scale_type_key)
|
||||
|
@ -52,6 +52,10 @@ class PreferencesHelper(context: Context) {
|
||||
|
||||
fun customBrightnessValue() = rxPrefs.getInteger(keys.customBrightnessValue, 0)
|
||||
|
||||
fun colorFilter() = rxPrefs.getBoolean(keys.colorFilter, false)
|
||||
|
||||
fun colorFilterValue() = rxPrefs.getInteger(keys.colorFilterValue, 0)
|
||||
|
||||
fun defaultViewer() = prefs.getInt(keys.defaultViewer, 1)
|
||||
|
||||
fun imageScaleType() = rxPrefs.getInteger(keys.imageScaleType, 1)
|
||||
|
@ -38,10 +38,12 @@ import me.zhanghai.android.systemuihelper.SystemUiHelper
|
||||
import me.zhanghai.android.systemuihelper.SystemUiHelper.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.DecimalFormat
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@RequiresPresenter(ReaderPresenter::class)
|
||||
class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
@ -70,6 +72,8 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
|
||||
private var customBrightnessSubscription: Subscription? = null
|
||||
|
||||
private var customFilterColorSubscription: Subscription? = null
|
||||
|
||||
var readerTheme: Int = 0
|
||||
private set
|
||||
|
||||
@ -140,6 +144,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_settings -> ReaderSettingsDialog().show(supportFragmentManager, "settings")
|
||||
R.id.action_custom_filter -> ReaderCustomFilterDialog().show(supportFragmentManager, "filter")
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
@ -354,9 +359,9 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
reader_menu_bottom.setOnTouchListener { v, event -> true }
|
||||
|
||||
page_seekbar.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
gotoPageInCurrentChapter(progress)
|
||||
gotoPageInCurrentChapter(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -378,6 +383,9 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
subscriptions += preferences.customBrightness().asObservable()
|
||||
.subscribe { setCustomBrightness(it) }
|
||||
|
||||
subscriptions += preferences.colorFilter().asObservable()
|
||||
.subscribe { setColorFilter(it) }
|
||||
|
||||
subscriptions += preferences.readerTheme().asObservable()
|
||||
.distinctUntilChanged()
|
||||
.subscribe { applyTheme(it) }
|
||||
@ -424,6 +432,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
private fun setCustomBrightness(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
customBrightnessSubscription = preferences.customBrightnessValue().asObservable()
|
||||
.sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
.subscribe { setCustomBrightnessValue(it) }
|
||||
|
||||
subscriptions.add(customBrightnessSubscription)
|
||||
@ -433,6 +442,19 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setColorFilter(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
customFilterColorSubscription = preferences.colorFilterValue().asObservable()
|
||||
.sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
.subscribe { setColorFilterValue(it) }
|
||||
|
||||
subscriptions.add(customFilterColorSubscription)
|
||||
} else {
|
||||
customFilterColorSubscription?.let { subscriptions.remove(it) }
|
||||
color_overlay.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the brightness of the screen. Range is [-75, 100].
|
||||
* From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness.
|
||||
@ -459,6 +481,11 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setColorFilterValue(value: Int) {
|
||||
color_overlay.visibility = View.VISIBLE
|
||||
color_overlay.setBackgroundColor(value)
|
||||
}
|
||||
|
||||
private fun applyTheme(theme: Int) {
|
||||
readerTheme = theme
|
||||
val rootView = window.decorView.rootView
|
||||
|
@ -0,0 +1,329 @@
|
||||
package eu.kanade.tachiyomi.ui.reader
|
||||
|
||||
import android.app.Dialog
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.annotation.ColorInt
|
||||
import android.support.v4.app.DialogFragment
|
||||
import android.view.View
|
||||
import android.widget.SeekBar
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.util.plusAssign
|
||||
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
|
||||
import kotlinx.android.synthetic.main.dialog_reader_custom_filter.view.*
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Custom dialog which can be used to set overlay value's
|
||||
*/
|
||||
class ReaderCustomFilterDialog : DialogFragment() {
|
||||
|
||||
companion object {
|
||||
/** Integer mask of alpha value **/
|
||||
private const val ALPHA_MASK: Long = 0xFF000000
|
||||
|
||||
/** Integer mask of red value **/
|
||||
private const val RED_MASK: Long = 0x00FF0000
|
||||
|
||||
/** Integer mask of green value **/
|
||||
private const val GREEN_MASK: Long = 0x0000FF00
|
||||
|
||||
/** Integer mask of blue value **/
|
||||
private const val BLUE_MASK: Long = 0x000000FF
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides operations to manage preferences
|
||||
*/
|
||||
private val preferences by injectLazy<PreferencesHelper>()
|
||||
|
||||
/**
|
||||
* Subscription used for filter overlay
|
||||
*/
|
||||
private lateinit var subscriptions: CompositeSubscription
|
||||
|
||||
/**
|
||||
* Subscription used for custom brightness overlay
|
||||
*/
|
||||
private var customBrightnessSubscription: Subscription? = null
|
||||
|
||||
/**
|
||||
* Subscription used for color filter overlay
|
||||
*/
|
||||
private var customFilterColorSubscription: Subscription? = null
|
||||
|
||||
/**
|
||||
* This method will be called after onCreate(Bundle)
|
||||
* @param savedState The last saved instance state of the Fragment.
|
||||
*/
|
||||
override fun onCreateDialog(savedState: Bundle?): Dialog {
|
||||
val dialog = MaterialDialog.Builder(activity)
|
||||
.customView(R.layout.dialog_reader_custom_filter, false)
|
||||
.positiveText(android.R.string.ok)
|
||||
.build()
|
||||
|
||||
subscriptions = CompositeSubscription()
|
||||
onViewCreated(dialog.view, savedState)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
/**
|
||||
* Called immediately after onCreateView()
|
||||
* @param view The View returned by onCreateDialog.
|
||||
* @param savedInstanceState If non-null, this fragment is being re-constructed
|
||||
*/
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) = with(view) {
|
||||
// Initialize subscriptions.
|
||||
subscriptions += preferences.colorFilter().asObservable()
|
||||
.subscribe { setColorFilter(it, view) }
|
||||
|
||||
subscriptions += preferences.customBrightness().asObservable()
|
||||
.subscribe { setCustomBrightness(it, view) }
|
||||
|
||||
// Get color and update values
|
||||
val color = preferences.colorFilterValue().getOrDefault()
|
||||
val brightness = preferences.customBrightnessValue().getOrDefault()
|
||||
|
||||
val argb = setValues(color, view)
|
||||
|
||||
// Set brightness value
|
||||
txt_brightness_seekbar_value.text = brightness.toString()
|
||||
|
||||
// Initialize seekBar progress
|
||||
seekbar_color_filter_alpha.progress = argb[0]
|
||||
seekbar_color_filter_red.progress = argb[1]
|
||||
seekbar_color_filter_green.progress = argb[2]
|
||||
seekbar_color_filter_blue.progress = argb[3]
|
||||
|
||||
// Set listeners
|
||||
switch_color_filter.isChecked = preferences.colorFilter().getOrDefault()
|
||||
switch_color_filter.setOnCheckedChangeListener { v, isChecked ->
|
||||
preferences.colorFilter().set(isChecked)
|
||||
}
|
||||
|
||||
custom_brightness.isChecked = preferences.customBrightness().getOrDefault()
|
||||
custom_brightness.setOnCheckedChangeListener { v, isChecked ->
|
||||
preferences.customBrightness().set(isChecked)
|
||||
}
|
||||
|
||||
seekbar_color_filter_alpha.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
setColorValue(value, ALPHA_MASK, 24)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
seekbar_color_filter_red.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
setColorValue(value, RED_MASK, 16)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
seekbar_color_filter_green.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
setColorValue(value, GREEN_MASK, 8)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
seekbar_color_filter_blue.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
setColorValue(value, BLUE_MASK, 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
brightness_seekbar.progress = preferences.customBrightnessValue().getOrDefault()
|
||||
brightness_seekbar.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
preferences.customBrightnessValue().set(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set enabled status of seekBars belonging to color filter
|
||||
* @param enabled determines if seekBar gets enabled
|
||||
* @param view view of the dialog
|
||||
*/
|
||||
private fun setColorFilterSeekBar(enabled: Boolean, view: View) = with(view) {
|
||||
seekbar_color_filter_red.isEnabled = enabled
|
||||
seekbar_color_filter_green.isEnabled = enabled
|
||||
seekbar_color_filter_blue.isEnabled = enabled
|
||||
seekbar_color_filter_alpha.isEnabled = enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Set enabled status of seekBars belonging to custom brightness
|
||||
* @param enabled value which determines if seekBar gets enabled
|
||||
* @param view view of the dialog
|
||||
*/
|
||||
private fun setCustomBrightnessSeekBar(enabled: Boolean, view: View) = with(view) {
|
||||
brightness_seekbar.isEnabled = enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text value's of color filter
|
||||
* @param color integer containing color information
|
||||
* @param view view of the dialog
|
||||
*/
|
||||
fun setValues(color: Int, view: View): Array<Int> {
|
||||
val alpha = getAlphaFromColor(color)
|
||||
val red = getRedFromColor(color)
|
||||
val green = getGreenFromColor(color)
|
||||
val blue = getBlueFromColor(color)
|
||||
|
||||
//Initialize values
|
||||
with(view) {
|
||||
txt_color_filter_alpha_value.text = alpha.toString()
|
||||
|
||||
txt_color_filter_red_value.text = red.toString()
|
||||
|
||||
txt_color_filter_green_value.text = green.toString()
|
||||
|
||||
txt_color_filter_blue_value.text = blue.toString()
|
||||
}
|
||||
return arrayOf(alpha, red, green, blue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the custom brightness value subscription
|
||||
* @param enabled determines if the subscription get (un)subscribed
|
||||
* @param view view of the dialog
|
||||
*/
|
||||
private fun setCustomBrightness(enabled: Boolean, view: View) {
|
||||
if (enabled) {
|
||||
customBrightnessSubscription = preferences.customBrightnessValue().asObservable()
|
||||
.sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
.subscribe { setCustomBrightnessValue(it, view) }
|
||||
|
||||
subscriptions.add(customBrightnessSubscription)
|
||||
} else {
|
||||
customBrightnessSubscription?.let { subscriptions.remove(it) }
|
||||
setCustomBrightnessValue(0, view, true)
|
||||
}
|
||||
setCustomBrightnessSeekBar(enabled, view)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the brightness of the screen. Range is [-75, 100].
|
||||
* From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness.
|
||||
* From 1 to 100 it sets that value as brightness.
|
||||
* 0 sets system brightness and hides the overlay.
|
||||
*/
|
||||
private fun setCustomBrightnessValue(value: Int, view: View, isDisabled: Boolean = false) = with(view) {
|
||||
// Set black overlay visibility.
|
||||
if (value < 0) {
|
||||
brightness_overlay.visibility = View.VISIBLE
|
||||
val alpha = (Math.abs(value) * 2.56).toInt()
|
||||
brightness_overlay.setBackgroundColor(Color.argb(alpha, 0, 0, 0))
|
||||
} else {
|
||||
brightness_overlay.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (!isDisabled)
|
||||
txt_brightness_seekbar_value.text = value.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the color filter value subscription
|
||||
* @param enabled determines if the subscription get (un)subscribed
|
||||
* @param view view of the dialog
|
||||
*/
|
||||
private fun setColorFilter(enabled: Boolean, view: View) {
|
||||
if (enabled) {
|
||||
customFilterColorSubscription = preferences.colorFilterValue().asObservable()
|
||||
.sample(100, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
.subscribe { setColorFilterValue(it, view) }
|
||||
|
||||
subscriptions.add(customFilterColorSubscription)
|
||||
} else {
|
||||
customFilterColorSubscription?.let { subscriptions.remove(it) }
|
||||
view.color_overlay.visibility = View.GONE
|
||||
}
|
||||
setColorFilterSeekBar(enabled, view)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color filter overlay of the screen. Determined by HEX of integer
|
||||
* @param color hex of color.
|
||||
* @param view view of the dialog
|
||||
*/
|
||||
private fun setColorFilterValue(@ColorInt color: Int, view: View) = with(view) {
|
||||
color_overlay.visibility = View.VISIBLE
|
||||
color_overlay.setBackgroundColor(color)
|
||||
setValues(color, view)
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the color value in preference
|
||||
* @param color value of color range [0,255]
|
||||
* @param mask contains hex mask of chosen color
|
||||
* @param bitShift amounts of bits that gets shifted to receive value
|
||||
*/
|
||||
fun setColorValue(color: Int, mask: Long, bitShift: Int) {
|
||||
val currentColor = preferences.colorFilterValue().getOrDefault()
|
||||
val updatedColor = (color shl bitShift) or (currentColor and mask.inv().toInt())
|
||||
preferences.colorFilterValue().set(updatedColor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alpha value from the Color Hex
|
||||
* @param color color hex as int
|
||||
* @return alpha of color
|
||||
*/
|
||||
fun getAlphaFromColor(color: Int): Int {
|
||||
return color shr 24 and 0xFF
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the red value from the Color Hex
|
||||
* @param color color hex as int
|
||||
* @return red of color
|
||||
*/
|
||||
fun getRedFromColor(color: Int): Int {
|
||||
return color shr 16 and 0xFF
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the green value from the Color Hex
|
||||
* @param color color hex as int
|
||||
* @return green of color
|
||||
*/
|
||||
fun getGreenFromColor(color: Int): Int {
|
||||
return color shr 8 and 0xFF
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the blue value from the Color Hex
|
||||
* @param color color hex as int
|
||||
* @return blue of color
|
||||
*/
|
||||
fun getBlueFromColor(color: Int): Int {
|
||||
return color and 0xFF
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when dialog is dismissed
|
||||
*/
|
||||
override fun onDestroyView() {
|
||||
subscriptions.unsubscribe()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,6 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.util.plusAssign
|
||||
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
|
||||
import kotlinx.android.synthetic.main.dialog_reader_settings.view.*
|
||||
import org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
@ -84,24 +83,6 @@ class ReaderSettingsDialog : DialogFragment() {
|
||||
fullscreen.setOnCheckedChangeListener { v, isChecked ->
|
||||
preferences.fullscreen().set(isChecked)
|
||||
}
|
||||
|
||||
custom_brightness.isChecked = preferences.customBrightness().getOrDefault()
|
||||
custom_brightness.setOnCheckedChangeListener { v, isChecked ->
|
||||
preferences.customBrightness().set(isChecked)
|
||||
}
|
||||
|
||||
brightness_seekbar.progress = preferences.customBrightnessValue().getOrDefault()
|
||||
brightness_seekbar.setOnProgressChangeListener(object : DiscreteSeekBar.OnProgressChangeListener {
|
||||
override fun onProgressChanged(seekBar: DiscreteSeekBar, value: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
preferences.customBrightnessValue().set(value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: DiscreteSeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: DiscreteSeekBar) {}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -0,0 +1,61 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.SeekBar
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
|
||||
class NegativeSeekBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
SeekBar(context, attrs) {
|
||||
|
||||
private var minValue: Int = 0
|
||||
private var maxValue: Int = 0
|
||||
private var listener: OnSeekBarChangeListener? = null
|
||||
|
||||
init {
|
||||
val styledAttributes = context.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.NegativeSeekBar, 0, 0)
|
||||
|
||||
try {
|
||||
setMinSeek(styledAttributes.getInt(R.styleable.NegativeSeekBar_min_seek, 0))
|
||||
setMaxSeek(styledAttributes.getInt(R.styleable.NegativeSeekBar_max_seek, 0))
|
||||
} finally {
|
||||
styledAttributes.recycle()
|
||||
}
|
||||
|
||||
super.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, value: Int, fromUser: Boolean) {
|
||||
listener?.let { it.onProgressChanged(seekBar, minValue + value, fromUser) }
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(p0: SeekBar?) {
|
||||
listener?.let { it.onStartTrackingTouch(p0) }
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(p0: SeekBar?) {
|
||||
listener?.let { it.onStopTrackingTouch(p0) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun setProgress(progress: Int) {
|
||||
super.setProgress(Math.abs(minValue) + progress)
|
||||
}
|
||||
|
||||
fun setMinSeek(minValue: Int) {
|
||||
this.minValue = minValue
|
||||
max = (this.maxValue - this.minValue)
|
||||
}
|
||||
|
||||
fun setMaxSeek(maxValue: Int) {
|
||||
this.maxValue = maxValue
|
||||
max = (this.maxValue - this.minValue)
|
||||
}
|
||||
|
||||
override fun setOnSeekBarChangeListener(listener: OnSeekBarChangeListener?) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.widget.SeekBar
|
||||
|
||||
open class SimpleSeekBarListener : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {}
|
||||
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
}
|
BIN
app/src/main/res/drawable/filter_mock.png
Normal file
BIN
app/src/main/res/drawable/filter_mock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 334 KiB |
9
app/src/main/res/drawable/ic_brightness_4_white_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_brightness_4_white_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-0.89,0 -1.74,-0.2 -2.5,-0.55C11.56,16.5 13,14.42 13,12s-1.44,-4.5 -3.5,-5.45C10.26,6.2 11.11,6 12,6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_brightness_5_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_brightness_5_black_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
|
||||
</vector>
|
@ -105,4 +105,10 @@
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/color_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</FrameLayout>
|
257
app/src/main/res/layout/dialog_reader_custom_filter.xml
Normal file
257
app/src/main/res/layout/dialog_reader_custom_filter.xml
Normal file
@ -0,0 +1,257 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="200dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/filter_mock" />
|
||||
|
||||
<View
|
||||
android:id="@+id/brightness_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<View
|
||||
android:id="@+id/color_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/material_component_dialogs_padding_around_content_area">
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:id="@+id/switch_color_filter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pref_custom_color_filter" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_red_symbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/color_filter_r_value"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_red_value"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_color_filter_red"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@id/txt_color_filter_red_symbol"
|
||||
android:layout_toLeftOf="@id/txt_color_filter_red_value"
|
||||
android:layout_toRightOf="@id/txt_color_filter_red_symbol"
|
||||
android:layout_toStartOf="@id/txt_color_filter_red_value"
|
||||
android:max="255"
|
||||
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_green_symbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/color_filter_g_value"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_green_value"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_color_filter_green"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@id/txt_color_filter_green_symbol"
|
||||
android:layout_toLeftOf="@id/txt_color_filter_green_value"
|
||||
android:layout_toRightOf="@id/txt_color_filter_green_symbol"
|
||||
android:layout_toStartOf="@id/txt_color_filter_green_value"
|
||||
android:max="255"
|
||||
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_blue_symbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/color_filter_b_value"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_blue_value"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_color_filter_blue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@id/txt_color_filter_blue_symbol"
|
||||
android:layout_toLeftOf="@id/txt_color_filter_blue_value"
|
||||
android:layout_toRightOf="@id/txt_color_filter_blue_symbol"
|
||||
android:layout_toStartOf="@id/txt_color_filter_blue_value"
|
||||
android:max="255"
|
||||
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_alpha_symbol"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/color_filter_a_value"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_color_filter_alpha_value"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar_color_filter_alpha"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@id/txt_color_filter_alpha_symbol"
|
||||
android:layout_toLeftOf="@id/txt_color_filter_alpha_value"
|
||||
android:layout_toRightOf="@id/txt_color_filter_alpha_symbol"
|
||||
android:layout_toStartOf="@id/txt_color_filter_alpha_value"
|
||||
android:max="255"
|
||||
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:id="@+id/custom_brightness"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/material_component_cards_primary_title_top_padding"
|
||||
android:text="@string/pref_custom_brightness" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:id="@+id/txt_brightness_seekbar_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary"
|
||||
android:tint="?android:attr/textColorSecondary"
|
||||
app:srcCompat="@drawable/ic_brightness_5_black_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txt_brightness_seekbar_value"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading.Secondary" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.NegativeSeekBar
|
||||
android:id="@+id/brightness_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_toEndOf="@id/txt_brightness_seekbar_icon"
|
||||
android:layout_toLeftOf="@id/txt_brightness_seekbar_value"
|
||||
android:layout_toRightOf="@id/txt_brightness_seekbar_icon"
|
||||
android:layout_toStartOf="@id/txt_brightness_seekbar_value"
|
||||
android:padding="@dimen/material_component_text_fields_floating_label_padding_between_label_and_input_text"
|
||||
app:max_seek="100"
|
||||
app:min_seek="-75" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:setting="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/material_component_dialogs_padding_around_content_area"
|
||||
android:divider="@drawable/empty_divider"
|
||||
@ -171,21 +171,4 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pref_fullscreen"/>
|
||||
|
||||
<android.support.v7.widget.SwitchCompat
|
||||
android:id="@+id/custom_brightness"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pref_custom_brightness"/>
|
||||
|
||||
<org.adw.library.widgets.discreteseekbar.DiscreteSeekBar
|
||||
android:id="@+id/brightness_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:dsb_min="-75"
|
||||
app:dsb_max="100"
|
||||
app:dsb_indicatorFormatter="%d%%"
|
||||
app:dsb_indicatorTextAppearance="@style/TextAppearance.Regular"
|
||||
app:dsb_indicatorColor="?colorAccent"
|
||||
app:dsb_progressColor="?colorAccent" />
|
||||
|
||||
</LinearLayout>
|
@ -2,11 +2,18 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_custom_filter"
|
||||
android:title="@string/label_settings"
|
||||
android:icon="@drawable/ic_brightness_4_white_24dp"
|
||||
app:showAsAction="ifRoom"
|
||||
/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:title="@string/label_settings"
|
||||
android:icon="@drawable/ic_settings_white_24dp"
|
||||
app:showAsAction="always"
|
||||
app:showAsAction="ifRoom"
|
||||
/>
|
||||
|
||||
</menu>
|
@ -17,6 +17,11 @@
|
||||
<attr name="max" format="integer"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="NegativeSeekBar">
|
||||
<attr name="min_seek" format="integer"/>
|
||||
<attr name="max_seek" format="integer"/>
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="navigation_view_theme" format="reference"/>
|
||||
<attr name="selectable_list_drawable" format="reference|integer" />
|
||||
<attr name="selectable_library_drawable" format="reference|integer"/>
|
||||
|
@ -32,6 +32,9 @@
|
||||
<string name="pref_keep_screen_on_key">pref_keep_screen_on_key</string>
|
||||
<string name="pref_custom_brightness_key">pref_custom_brightness_key</string>
|
||||
<string name="pref_custom_brightness_value_key">custom_brightness_value</string>
|
||||
<string name="pref_color_filter_key">pref_color_filter_key</string>
|
||||
<string name="pref_color_filter_value_key">color_filter_value</string>
|
||||
<string name="pref_red_filter_value_key">pref_red_filter_value</string>
|
||||
<string name="pref_reader_theme_key">pref_reader_theme_key</string>
|
||||
<string name="pref_image_decoder_key">pref_image_decoder_key</string>
|
||||
<string name="pref_read_with_volume_keys_key">reader_volume_keys</string>
|
||||
|
@ -105,6 +105,7 @@
|
||||
<string name="pref_enable_transitions">Enable transitions</string>
|
||||
<string name="pref_show_page_number">Show page number</string>
|
||||
<string name="pref_custom_brightness">Use custom brightness</string>
|
||||
<string name="pref_custom_color_filter">Use custom color filter</string>
|
||||
<string name="pref_keep_screen_on">Keep screen on</string>
|
||||
<string name="pref_reader_navigation">Navigation</string>
|
||||
<string name="pref_read_with_volume_keys">Volume keys</string>
|
||||
@ -138,6 +139,11 @@
|
||||
<string name="rotation_lock">Lock</string>
|
||||
<string name="rotation_force_portrait">Force portrait</string>
|
||||
<string name="rotation_force_landscape">Force landscape</string>
|
||||
<string name="color_filter_r_value">R</string>
|
||||
<string name="color_filter_g_value">G</string>
|
||||
<string name="color_filter_b_value">B</string>
|
||||
<string name="color_filter_a_value">A</string>
|
||||
|
||||
|
||||
<!-- Downloads section -->
|
||||
<string name="pref_download_directory">Downloads directory</string>
|
||||
|
@ -91,6 +91,10 @@
|
||||
<item name="android:textSize">16sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Regular.SubHeading.Secondary">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Regular.SubHeading.Light">
|
||||
<item name="android:textColor">@color/textColorPrimaryDark</item>
|
||||
</style>
|
||||
@ -107,6 +111,10 @@
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Medium.Title.Secondary">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Medium.Body2">
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
@ -69,11 +69,6 @@
|
||||
android:key="@string/pref_show_page_number_key"
|
||||
android:defaultValue="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/pref_custom_brightness"
|
||||
android:key="@string/pref_custom_brightness_key"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:title="@string/pref_keep_screen_on"
|
||||
android:key="@string/pref_keep_screen_on_key"
|
||||
|
Loading…
Reference in New Issue
Block a user