Skip to content

Commit 71587d8

Browse files
authored
Merge pull request #31 from Softwee/feature/bugfixes
New customization, bugfixes & cleanup
2 parents 40f7891 + b44ef49 commit 71587d8

File tree

23 files changed

+439
-235
lines changed

23 files changed

+439
-235
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ android:
1010
- tools # to get the new `repository-11.xml`
1111
- tools # to install latest Android SDK tools
1212
- platform-tools
13-
- build-tools-25.0.1
13+
- build-tools-25.0.3
1414
- android-25
1515
- extra-android-m2repository # Design Support library
16+
- android-21
1617
- sys-img-armeabi-v7a-android-21 # system image (emulator)
1718
licenses:
1819
- android-sdk-license-.+

codeview/build.gradle

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ android {
99
minSdkVersion 15
1010
targetSdkVersion 25
1111
versionCode 1
12-
versionName '1.0'
12+
versionName '1.3.0'
1313
}
1414
buildTypes {
1515
release {
1616
minifyEnabled false
1717
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
1818
}
1919
}
20-
sourceSets {
21-
main.java.srcDirs += 'src/main/kotlin'
20+
lintOptions {
21+
abortOnError false
2222
}
2323
}
2424

@@ -28,8 +28,3 @@ dependencies {
2828
compile 'com.android.support:appcompat-v7:25.3.1'
2929
compile 'com.android.support:recyclerview-v7:25.3.1'
3030
}
31-
repositories {
32-
mavenCentral()
33-
}
34-
buildscript {
35-
}
96.2 KB
Binary file not shown.
674 KB
Binary file not shown.
327 KB
Binary file not shown.
93.7 KB
Binary file not shown.
58.5 KB
Binary file not shown.

codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.github.kbiakov.codeview
22

33
import android.content.Context
4+
import android.graphics.drawable.Drawable
5+
import android.graphics.drawable.GradientDrawable
46
import android.support.v7.widget.LinearLayoutManager
57
import android.support.v7.widget.RecyclerView
68
import android.util.AttributeSet
@@ -10,6 +12,8 @@ import io.github.kbiakov.codeview.Thread.delayed
1012
import io.github.kbiakov.codeview.adapters.AbstractCodeAdapter
1113
import io.github.kbiakov.codeview.adapters.CodeWithNotesAdapter
1214
import io.github.kbiakov.codeview.adapters.Options
15+
import io.github.kbiakov.codeview.highlight.ColorThemeData
16+
import io.github.kbiakov.codeview.highlight.color
1317

1418
/**
1519
* @class CodeView
@@ -20,52 +24,58 @@ import io.github.kbiakov.codeview.adapters.Options
2024
*/
2125
class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {
2226

23-
private val vShadowRight: View
24-
private val vShadowBottomLine: View
25-
private val vShadowBottomContent: View
26-
2727
private val vCodeList: RecyclerView
28+
private val vShadows: Map<ShadowPosition, View>
2829

2930
/**
3031
* Primary constructor.
3132
*/
3233
init {
3334
inflate(context, R.layout.layout_code_view, this)
35+
checkStartAnimation(attrs)
36+
37+
vCodeList = findViewById(R.id.rv_code_content) as RecyclerView
38+
vCodeList.layoutManager = LinearLayoutManager(context)
39+
vCodeList.isNestedScrollingEnabled = true
40+
41+
vShadows = mapOf(
42+
ShadowPosition.RightBorder to R.id.shadow_right_border,
43+
ShadowPosition.NumBottom to R.id.shadow_num_bottom,
44+
ShadowPosition.ContentBottom to R.id.shadow_content_bottom
45+
).mapValues { findViewById(it.value) }
46+
}
3447

35-
if (visibility == VISIBLE && isAnimateOnStart(context, attrs)) {
48+
private fun checkStartAnimation(attrs: AttributeSet) {
49+
if (visibility == VISIBLE && attrs.isAnimateOnStart(context)) {
3650
alpha = Const.Alpha.Invisible
3751

3852
animate()
3953
.setDuration(Const.DefaultDelay * 5)
4054
.alpha(Const.Alpha.Initial)
41-
} else {
55+
} else
4256
alpha = Const.Alpha.Initial
43-
}
44-
45-
// TODO: add shadow color customization
46-
vShadowRight = findViewById(R.id.v_shadow_right)
47-
vShadowBottomLine = findViewById(R.id.v_shadow_bottom_line)
48-
vShadowBottomContent = findViewById(R.id.v_shadow_bottom_content)
57+
}
4958

50-
vCodeList = findViewById(R.id.rv_code_content) as RecyclerView
51-
vCodeList.layoutManager = LinearLayoutManager(context)
52-
vCodeList.isNestedScrollingEnabled = true
59+
private fun AbstractCodeAdapter<*>.checkHighlightAnimation(action: () -> Unit) {
60+
if (options.animateOnHighlight) {
61+
animate()
62+
.setDuration(Const.DefaultDelay * 2)
63+
.alpha(Const.Alpha.AlmostInvisible)
64+
delayed {
65+
animate().alpha(Const.Alpha.Visible)
66+
action()
67+
}
68+
} else action()
5369
}
5470

5571
/**
5672
* Highlight code with defined programming language.
5773
* It holds the placeholder on view until code is not highlighted.
5874
*/
5975
private fun highlight() {
60-
getAdapter()?.highlight {
61-
62-
animate()
63-
.setDuration(Const.DefaultDelay * 2)
64-
.alpha(.1f)
65-
66-
delayed {
67-
animate().alpha(1f)
68-
getAdapter()?.notifyDataSetChanged()
76+
getAdapter()?.apply {
77+
highlight {
78+
checkHighlightAnimation(this::notifyDataSetChanged)
6979
}
7080
}
7181
}
@@ -74,14 +84,15 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
7484
* Border shadows will shown if full listing presented.
7585
* It helps to see what part of code is scrolled & hidden.
7686
*
77-
* @param isShadows Is shadows needed
87+
* @param isVisible Is shadows visible
7888
*/
79-
private fun setupShadows(isShadows: Boolean) {
80-
val visibility = if (isShadows) VISIBLE else GONE
81-
82-
vShadowRight.visibility = visibility
83-
vShadowBottomLine.visibility = visibility
84-
vShadowBottomContent.visibility = visibility
89+
fun setupShadows(isVisible: Boolean) {
90+
val visibility = if (isVisible) VISIBLE else GONE
91+
val theme = getOptionsOrDefault().theme
92+
vShadows.forEach { (pos, view) ->
93+
view.visibility = visibility
94+
view.setSafeBackground(pos.createShadow(theme))
95+
}
8596
}
8697

8798
// - Initialization
@@ -105,7 +116,6 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
105116
*/
106117
fun setAdapter(adapter: AbstractCodeAdapter<*>) {
107118
vCodeList.adapter = adapter
108-
setupShadows(adapter.options.shadows)
109119
highlight()
110120
}
111121

@@ -125,6 +135,12 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
125135
fun updateOptions(options: Options) {
126136
getAdapter() ?: setOptions(options)
127137
getAdapter()?.options = options
138+
setupShadows(options.shadows)
139+
}
140+
141+
fun updateOptions(body: Options.() -> Unit) {
142+
val options = getOptions() ?: getOptionsOrDefault()
143+
updateOptions(options.apply(body))
128144
}
129145

130146
// - Adapter
@@ -181,14 +197,35 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
181197

182198
companion object {
183199

184-
private fun isAnimateOnStart(context: Context, attr: AttributeSet): Boolean {
185-
context.theme.obtainStyledAttributes(attr, R.styleable.CodeView, 0, 0).apply {
200+
private fun AttributeSet.isAnimateOnStart(context: Context): Boolean {
201+
context.theme.obtainStyledAttributes(this, R.styleable.CodeView, 0, 0).apply {
186202
val flag = getBoolean(R.styleable.CodeView_animateOnStart, false)
187203
recycle()
188204
return@isAnimateOnStart flag
189205
}
190206
return false
191207
}
208+
209+
private fun View.setSafeBackground(newBackground: Drawable) {
210+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
211+
background = newBackground
212+
}
213+
}
214+
}
215+
216+
private enum class ShadowPosition {
217+
RightBorder,
218+
NumBottom,
219+
ContentBottom;
220+
221+
fun createShadow(theme: ColorThemeData) = when (this) {
222+
RightBorder -> GradientDrawable.Orientation.LEFT_RIGHT to theme.bgContent
223+
NumBottom -> GradientDrawable.Orientation.TOP_BOTTOM to theme.bgNum
224+
ContentBottom -> GradientDrawable.Orientation.TOP_BOTTOM to theme.bgContent
225+
}.let {
226+
val colors = arrayOf(android.R.color.transparent, it.second)
227+
GradientDrawable(it.first, colors.map(Int::color).toIntArray())
228+
}
192229
}
193230
}
194231

codeview/src/main/java/io/github/kbiakov/codeview/Utils.kt

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ import android.content.Context
44
import android.os.Handler
55
import android.os.Looper
66
import android.text.Html
7+
import android.text.SpannableString
78
import android.text.Spanned
9+
import android.text.TextUtils
810
import android.util.TypedValue
911
import java.io.BufferedReader
1012
import java.io.InputStreamReader
11-
import java.util.*
1213
import java.util.concurrent.Executors
1314

1415
object Const {
1516
val DefaultDelay = 250L
1617

1718
object Alpha {
19+
val Visible = 1f
1820
val Initial = 0.7f
21+
22+
val AlmostInvisible = 0.1f
1923
val Invisible = 0f
20-
val Visible = 1f
2124
}
2225
}
2326

@@ -54,7 +57,7 @@ fun extractLines(source: String) = listOf(*source.split("\n").toTypedArray())
5457
* @param idx Index to slice
5558
* @return Pair of lists with head and tail
5659
*/
57-
fun <T> List<T>.slice(idx: Int) = Pair(this.subList(0, idx), this.subList(idx, this.lastIndex))
60+
fun <T> List<T>.slice(idx: Int) = Pair(subList(0, idx), subList(idx, lastIndex))
5861

5962
/**
6063
* Get HTML from string.
@@ -63,13 +66,21 @@ fun <T> List<T>.slice(idx: Int) = Pair(this.subList(0, idx), this.subList(idx, t
6366
* @return Spanned HTML string
6467
*/
6568
@Suppress("deprecation")
66-
fun html(content: String) =
67-
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
68-
Html.fromHtml(content.htmlSafe(), Html.FROM_HTML_MODE_LEGACY)
69-
else
70-
Html.fromHtml(content.htmlSafe())
69+
fun html(content: String): Spanned {
70+
val spanned = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N)
71+
Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY)
72+
else
73+
Html.fromHtml(content)
74+
val spaces = content.startSpacesForTaggedString()
75+
return SpannableString(TextUtils.concat(spaces, spanned))
76+
}
7177

72-
fun String.htmlSafe() = replace(" ", "&nbsp;")
78+
private fun String.startSpacesForTaggedString(): String {
79+
val startIdx = indexOf('>') + 1
80+
val escaped = substring(startIdx)
81+
val count = escaped.indexOf(escaped.trim())
82+
return " ".repeat(count)
83+
}
7384

7485
object Thread {
7586
/**

0 commit comments

Comments
 (0)