Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ android:
- tools # to get the new `repository-11.xml`
- tools # to install latest Android SDK tools
- platform-tools
- build-tools-25.0.1
- build-tools-25.0.3
- android-25
- extra-android-m2repository # Design Support library
- android-21
- sys-img-armeabi-v7a-android-21 # system image (emulator)
licenses:
- android-sdk-license-.+
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.0.3'
ext.kotlin_version = '1.1.2-5'
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.android.tools.build:gradle:2.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

// NOTE: Do not place your application dependencies here; they belong
Expand Down
17 changes: 6 additions & 11 deletions codeview/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,28 @@ apply plugin: 'kotlin-android'

android {
compileSdkVersion 25
buildToolsVersion "25.0.1"
buildToolsVersion '25.0.3'

defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
versionName '1.3.0'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
lintOptions {
abortOnError false
}
}

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:recyclerview-v7:25.0.1'
}
repositories {
mavenCentral()
}
buildscript {
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
}
1 change: 0 additions & 1 deletion codeview/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">

Expand Down
Binary file added codeview/src/main/assets/fonts/Consolas.ttf
Binary file not shown.
Binary file added codeview/src/main/assets/fonts/CourierNew.ttf
Binary file not shown.
Binary file not shown.
Binary file added codeview/src/main/assets/fonts/Inconsolata.ttf
Binary file not shown.
Binary file added codeview/src/main/assets/fonts/Monaco.ttf
Binary file not shown.
138 changes: 89 additions & 49 deletions codeview/src/main/java/io/github/kbiakov/codeview/CodeView.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.github.kbiakov.codeview

import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.AttributeSet
Expand All @@ -10,6 +12,8 @@ import io.github.kbiakov.codeview.Thread.delayed
import io.github.kbiakov.codeview.adapters.AbstractCodeAdapter
import io.github.kbiakov.codeview.adapters.CodeWithNotesAdapter
import io.github.kbiakov.codeview.adapters.Options
import io.github.kbiakov.codeview.highlight.ColorThemeData
import io.github.kbiakov.codeview.highlight.color

/**
* @class CodeView
Expand All @@ -20,59 +24,58 @@ import io.github.kbiakov.codeview.adapters.Options
*/
class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {

private val vShadowRight: View
private val vShadowBottomLine: View
private val vShadowBottomContent: View

private val vCodeList: RecyclerView
private val vShadows: Map<ShadowPosition, View>

/**
* Primary constructor.
*/
init {
val isAnimateOnStart = visibility == VISIBLE && { ctx: Context, ats: AttributeSet ->
val a = ctx.theme.obtainStyledAttributes(ats, R.styleable.CodeView, 0, 0)
inflate(context, R.layout.layout_code_view, this)
checkStartAnimation(attrs)

try {
a.getBoolean(R.styleable.CodeView_animateOnStart, true)
} finally {
a.recycle()
}
}(context, attrs)
vCodeList = findViewById(R.id.rv_code_content) as RecyclerView
vCodeList.layoutManager = LinearLayoutManager(context)
vCodeList.isNestedScrollingEnabled = true

alpha = if (isAnimateOnStart) 0f else Consts.ALPHA
vShadows = mapOf(
ShadowPosition.RightBorder to R.id.shadow_right_border,
ShadowPosition.NumBottom to R.id.shadow_num_bottom,
ShadowPosition.ContentBottom to R.id.shadow_content_bottom
).mapValues { findViewById(it.value) }
}

inflate(context, R.layout.layout_code_view, this)
private fun checkStartAnimation(attrs: AttributeSet) {
if (visibility == VISIBLE && attrs.isAnimateOnStart(context)) {
alpha = Const.Alpha.Invisible

if (isAnimateOnStart)
animate()
.setDuration(Consts.DELAY * 5)
.alpha(Consts.ALPHA)

// TODO: add shadow color customization
vShadowRight = findViewById(R.id.v_shadow_right)
vShadowBottomLine = findViewById(R.id.v_shadow_bottom_line)
vShadowBottomContent = findViewById(R.id.v_shadow_bottom_content)
.setDuration(Const.DefaultDelay * 5)
.alpha(Const.Alpha.Initial)
} else
alpha = Const.Alpha.Initial
}

vCodeList = findViewById(R.id.rv_code_content) as RecyclerView
vCodeList.layoutManager = LinearLayoutManager(context)
vCodeList.isNestedScrollingEnabled = true
private fun AbstractCodeAdapter<*>.checkHighlightAnimation(action: () -> Unit) {
if (options.animateOnHighlight) {
animate()
.setDuration(Const.DefaultDelay * 2)
.alpha(Const.Alpha.AlmostInvisible)
delayed {
animate().alpha(Const.Alpha.Visible)
action()
}
} else action()
}

/**
* Highlight code with defined programming language.
* It holds the placeholder on view until code is not highlighted.
*/
private fun highlight() {
getAdapter()?.highlight {

animate()
.setDuration(Consts.DELAY * 2)
.alpha(.1f)

delayed {
animate().alpha(1f)
getAdapter()?.notifyDataSetChanged()
getAdapter()?.apply {
highlight {
checkHighlightAnimation(this::notifyDataSetChanged)
}
}
}
Expand All @@ -81,14 +84,15 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
* Border shadows will shown if full listing presented.
* It helps to see what part of code is scrolled & hidden.
*
* @param isShadows Is shadows needed
* @param isVisible Is shadows visible
*/
private fun setupShadows(isShadows: Boolean) {
val visibility = if (isShadows) VISIBLE else GONE

vShadowRight.visibility = visibility
vShadowBottomLine.visibility = visibility
vShadowBottomContent.visibility = visibility
fun setupShadows(isVisible: Boolean) {
val visibility = if (isVisible) VISIBLE else GONE
val theme = getOptionsOrDefault().theme
vShadows.forEach { (pos, view) ->
view.visibility = visibility
view.setSafeBackground(pos.createShadow(theme))
}
}

// - Initialization
Expand All @@ -112,7 +116,6 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
*/
fun setAdapter(adapter: AbstractCodeAdapter<*>) {
vCodeList.adapter = adapter
setupShadows(adapter.options.shadows)
highlight()
}

Expand All @@ -121,7 +124,7 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
/**
* View options accessor.
*/
fun getOptions(): Options? = getAdapter()?.options
fun getOptions() = getAdapter()?.options
fun getOptionsOrDefault() = getOptions() ?: Options(context)

/**
Expand All @@ -130,10 +133,14 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
* @param options Options
*/
fun updateOptions(options: Options) {
if (getAdapter() == null)
setOptions(options)
else
getAdapter()!!.options = options
getAdapter() ?: setOptions(options)
getAdapter()?.options = options
setupShadows(options.shadows)
}

fun updateOptions(body: Options.() -> Unit) {
val options = getOptions() ?: getOptionsOrDefault()
updateOptions(options.apply(body))
}

// - Adapter
Expand Down Expand Up @@ -169,7 +176,7 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
*/
fun setCode(code: String) {
getAdapter() ?: prepare()
getAdapter()!!.updateCode(code)
getAdapter()?.updateCode(code)
}

/**
Expand All @@ -185,7 +192,40 @@ class CodeView(context: Context, attrs: AttributeSet) : RelativeLayout(context,
fun setCode(code: String, language: String) {
val options = getOptionsOrDefault()
updateOptions(options.withLanguage(language))
getAdapter()!!.updateCode(code)
getAdapter()?.updateCode(code)
}

companion object {

private fun AttributeSet.isAnimateOnStart(context: Context): Boolean {
context.theme.obtainStyledAttributes(this, R.styleable.CodeView, 0, 0).apply {
val flag = getBoolean(R.styleable.CodeView_animateOnStart, false)
recycle()
return@isAnimateOnStart flag
}
return false
}

private fun View.setSafeBackground(newBackground: Drawable) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
background = newBackground
}
}
}

private enum class ShadowPosition {
RightBorder,
NumBottom,
ContentBottom;

fun createShadow(theme: ColorThemeData) = when (this) {
RightBorder -> GradientDrawable.Orientation.LEFT_RIGHT to theme.bgContent
NumBottom -> GradientDrawable.Orientation.TOP_BOTTOM to theme.bgNum
ContentBottom -> GradientDrawable.Orientation.TOP_BOTTOM to theme.bgContent
}.let {
val colors = arrayOf(android.R.color.transparent, it.second)
GradientDrawable(it.first, colors.map(Int::color).toIntArray())
}
}
}

Expand Down
Loading