Keyboard markup (#203)
|
@ -34,6 +34,7 @@ dependencies {
|
|||
implementation applicationDependencies.appcompat
|
||||
implementation applicationDependencies.kotlin
|
||||
implementation applicationDependencies.coroutines
|
||||
implementation applicationDependencies.androidxCore
|
||||
|
||||
implementation applicationDependencies.design
|
||||
implementation applicationDependencies.recyclerView
|
||||
|
|
|
@ -2,10 +2,13 @@ package com.agileburo.anytype.core_ui.common
|
|||
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.text.Annotation
|
||||
import android.text.Editable
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.*
|
||||
import com.agileburo.anytype.core_ui.widgets.text.KEY_ROUNDED
|
||||
import com.agileburo.anytype.core_ui.widgets.text.VALUE_ROUNDED
|
||||
|
||||
/**
|
||||
* Classes implementing this interface should support markup rendering.
|
||||
|
@ -47,11 +50,13 @@ interface Markup {
|
|||
STRIKETHROUGH,
|
||||
TEXT_COLOR,
|
||||
BACKGROUND_COLOR,
|
||||
LINK
|
||||
LINK,
|
||||
KEYBOARD
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_SPANNABLE_FLAG = Spannable.SPAN_EXCLUSIVE_INCLUSIVE
|
||||
const val SPAN_MONOSPACE = "monospace"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +99,19 @@ fun Markup.toSpannable() = SpannableString(body).apply {
|
|||
mark.to,
|
||||
Markup.DEFAULT_SPANNABLE_FLAG
|
||||
)
|
||||
Markup.Type.KEYBOARD -> setSpan(
|
||||
TypefaceSpan(Markup.SPAN_MONOSPACE),
|
||||
mark.from,
|
||||
mark.to,
|
||||
Markup.DEFAULT_SPANNABLE_FLAG
|
||||
).also {
|
||||
setSpan(
|
||||
Annotation(KEY_ROUNDED, VALUE_ROUNDED),
|
||||
mark.from,
|
||||
mark.to,
|
||||
Markup.DEFAULT_SPANNABLE_FLAG
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +120,9 @@ fun Editable.setMarkup(markup: Markup) {
|
|||
getSpans(0, length, CharacterStyle::class.java).forEach { span ->
|
||||
removeSpan(span)
|
||||
}
|
||||
getSpans(0, length, Annotation::class.java).forEach { span ->
|
||||
removeSpan(span)
|
||||
}
|
||||
markup.marks.forEach { mark ->
|
||||
when (mark.type) {
|
||||
Markup.Type.ITALIC -> setSpan(
|
||||
|
@ -142,6 +163,21 @@ fun Editable.setMarkup(markup: Markup) {
|
|||
Markup.DEFAULT_SPANNABLE_FLAG
|
||||
)
|
||||
}
|
||||
Markup.Type.KEYBOARD -> {
|
||||
setSpan(
|
||||
TypefaceSpan(Markup.SPAN_MONOSPACE),
|
||||
mark.from,
|
||||
mark.to,
|
||||
Markup.DEFAULT_SPANNABLE_FLAG
|
||||
).also {
|
||||
setSpan(
|
||||
Annotation(KEY_ROUNDED, VALUE_ROUNDED),
|
||||
mark.from,
|
||||
mark.to,
|
||||
Markup.DEFAULT_SPANNABLE_FLAG
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.agileburo.anytype.core_ui.widgets.text
|
||||
|
||||
import android.os.Build
|
||||
import android.text.Layout
|
||||
|
||||
// Extension functions for Layout object
|
||||
|
||||
/**
|
||||
* Android system default line spacing extra
|
||||
*/
|
||||
private const val DEFAULT_LINESPACING_EXTRA = 0f
|
||||
|
||||
/**
|
||||
* Android system default line spacing multiplier
|
||||
*/
|
||||
private const val DEFAULT_LINESPACING_MULTIPLIER = 1f
|
||||
|
||||
/**
|
||||
* Get the line bottom discarding the line spacing added.
|
||||
*/
|
||||
fun Layout.getLineBottomWithoutSpacing(line: Int): Int {
|
||||
val lineBottom = getLineBottom(line)
|
||||
val lastLineSpacingNotAdded = Build.VERSION.SDK_INT >= 19
|
||||
val isLastLine = line == lineCount - 1
|
||||
|
||||
val lineBottomWithoutSpacing: Int
|
||||
val lineSpacingExtra = spacingAdd
|
||||
val lineSpacingMultiplier = spacingMultiplier
|
||||
val hasLineSpacing = lineSpacingExtra != DEFAULT_LINESPACING_EXTRA
|
||||
|| lineSpacingMultiplier != DEFAULT_LINESPACING_MULTIPLIER
|
||||
|
||||
if (!hasLineSpacing || isLastLine && lastLineSpacingNotAdded) {
|
||||
lineBottomWithoutSpacing = lineBottom
|
||||
} else {
|
||||
val extra: Float
|
||||
if (lineSpacingMultiplier.compareTo(DEFAULT_LINESPACING_MULTIPLIER) != 0) {
|
||||
val lineHeight = getLineHeight(line)
|
||||
extra = lineHeight - (lineHeight - lineSpacingExtra) / lineSpacingMultiplier
|
||||
} else {
|
||||
extra = lineSpacingExtra
|
||||
}
|
||||
|
||||
lineBottomWithoutSpacing = (lineBottom - extra).toInt()
|
||||
}
|
||||
|
||||
return lineBottomWithoutSpacing
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the line height of a line.
|
||||
*/
|
||||
fun Layout.getLineHeight(line: Int): Int {
|
||||
return getLineTop(line + 1) - getLineTop(line)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top of the Layout after removing the extra padding applied by the Layout.
|
||||
*/
|
||||
fun Layout.getLineTopWithoutPadding(line: Int): Int {
|
||||
var lineTop = getLineTop(line)
|
||||
if (line == 0) {
|
||||
lineTop -= topPadding
|
||||
}
|
||||
return lineTop
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bottom of the Layout after removing the extra padding applied by the Layout.
|
||||
*/
|
||||
fun Layout.getLineBottomWithoutPadding(line: Int): Int {
|
||||
var lineBottom = getLineBottomWithoutSpacing(line)
|
||||
if (line == lineCount - 1) {
|
||||
lineBottom -= bottomPadding
|
||||
}
|
||||
return lineBottom
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
package com.agileburo.anytype.core_ui.widgets.text
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.text.Spanned
|
||||
import android.text.TextWatcher
|
||||
import android.text.util.Linkify
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import androidx.core.graphics.withTranslation
|
||||
import com.agileburo.anytype.core_ui.extensions.toast
|
||||
import me.saket.bettermovementmethod.BetterLinkMovementMethod
|
||||
import timber.log.Timber
|
||||
|
@ -12,16 +15,29 @@ import timber.log.Timber
|
|||
class TextInputWidget : AppCompatEditText {
|
||||
|
||||
private val watchers: MutableList<TextWatcher> = mutableListOf()
|
||||
private val textRoundedBgHelper: TextRoundedBgHelper
|
||||
|
||||
var selectionDetector: ((IntRange) -> Unit)? = null
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet,
|
||||
defStyle: Int = android.R.attr.textViewStyle) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyle
|
||||
)
|
||||
) {
|
||||
val attributeReader = TextRoundedBgAttributeReader(context, attrs)
|
||||
textRoundedBgHelper = TextRoundedBgHelper(
|
||||
horizontalPadding = attributeReader.horizontalPadding,
|
||||
verticalPadding = attributeReader.verticalPadding,
|
||||
drawable = attributeReader.drawable,
|
||||
drawableLeft = attributeReader.drawableLeft,
|
||||
drawableMid = attributeReader.drawableMid,
|
||||
drawableRight = attributeReader.drawableRight
|
||||
)
|
||||
}
|
||||
|
||||
override fun addTextChangedListener(watcher: TextWatcher) {
|
||||
watchers.add(watcher)
|
||||
|
@ -44,6 +60,16 @@ class TextInputWidget : AppCompatEditText {
|
|||
super.onSelectionChanged(selStart, selEnd)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas?) {
|
||||
// need to draw bg first so that text can be on top during super.onDraw()
|
||||
if (text is Spanned && layout != null) {
|
||||
canvas?.withTranslation(totalPaddingLeft.toFloat(), totalPaddingTop.toFloat()) {
|
||||
textRoundedBgHelper.draw(canvas, text as Spanned, layout)
|
||||
}
|
||||
}
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
|
||||
fun setLinksClickable() {
|
||||
//makeLinksActive()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.agileburo.anytype.core_ui.widgets.text
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.content.res.getDrawableOrThrow
|
||||
import com.agileburo.anytype.core_ui.R
|
||||
|
||||
/**
|
||||
* Reads default attributes that [TextRoundedBgHelper] needs from resources. The attributes read
|
||||
* are:
|
||||
*
|
||||
* - chHorizontalPadding: the padding to be applied to left & right of the background
|
||||
* - chVerticalPadding: the padding to be applied to top & bottom of the background
|
||||
* - chDrawable: the drawable used to draw the background
|
||||
* - chDrawableLeft: the drawable used to draw left edge of the background
|
||||
* - chDrawableMid: the drawable used to draw for whole line
|
||||
* - chDrawableRight: the drawable used to draw right edge of the background
|
||||
*/
|
||||
class TextRoundedBgAttributeReader(context: Context, attrs: AttributeSet?) {
|
||||
|
||||
val horizontalPadding: Int
|
||||
val verticalPadding: Int
|
||||
val drawable: Drawable
|
||||
val drawableLeft: Drawable
|
||||
val drawableMid: Drawable
|
||||
val drawableRight: Drawable
|
||||
|
||||
init {
|
||||
val typedArray = context.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.TextRoundedBgHelper,
|
||||
0,
|
||||
R.style.RoundedBgTextView
|
||||
)
|
||||
horizontalPadding = typedArray.getDimensionPixelSize(
|
||||
R.styleable.TextRoundedBgHelper_roundedTextHorizontalPadding,
|
||||
0
|
||||
)
|
||||
verticalPadding = typedArray.getDimensionPixelSize(
|
||||
R.styleable.TextRoundedBgHelper_roundedTextVerticalPadding,
|
||||
0
|
||||
)
|
||||
drawable = typedArray.getDrawableOrThrow(
|
||||
R.styleable.TextRoundedBgHelper_roundedTextDrawable
|
||||
)
|
||||
drawableLeft = typedArray.getDrawableOrThrow(
|
||||
R.styleable.TextRoundedBgHelper_roundedTextDrawableLeft
|
||||
)
|
||||
drawableMid = typedArray.getDrawableOrThrow(
|
||||
R.styleable.TextRoundedBgHelper_roundedTextDrawableMid
|
||||
)
|
||||
drawableRight = typedArray.getDrawableOrThrow(
|
||||
R.styleable.TextRoundedBgHelper_roundedTextDrawableRight
|
||||
)
|
||||
typedArray.recycle()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package com.agileburo.anytype.core_ui.widgets.text
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Annotation
|
||||
import android.text.Layout
|
||||
import android.text.Spanned
|
||||
|
||||
/**
|
||||
* Helper class to draw multi-line rounded background to certain parts of a text. The start/end
|
||||
* positions of the backgrounds are annotated with [android.text.Annotation] class. Each annotation
|
||||
* should have the annotation key set to **rounded**.
|
||||
*
|
||||
* i.e.:
|
||||
* ```
|
||||
* <!--without the quotes at the begining and end Android strips the whitespace and also starts
|
||||
* the annotation at the wrong position-->
|
||||
* <string name="ltr">"this is <annotation key="rounded">a regular</annotation> paragraph."</string>
|
||||
* ```
|
||||
*
|
||||
* **Note:** BiDi text is not supported.
|
||||
*
|
||||
* @param horizontalPadding the padding to be applied to left & right of the background
|
||||
* @param verticalPadding the padding to be applied to top & bottom of the background
|
||||
* @param drawable the drawable used to draw the background
|
||||
* @param drawableLeft the drawable used to draw left edge of the background
|
||||
* @param drawableMid the drawable used to draw for whole line
|
||||
* @param drawableRight the drawable used to draw right edge of the background
|
||||
*/
|
||||
const val KEY_ROUNDED = "key"
|
||||
const val VALUE_ROUNDED = "rounded"
|
||||
|
||||
class TextRoundedBgHelper(
|
||||
val horizontalPadding: Int,
|
||||
verticalPadding: Int,
|
||||
drawable: Drawable,
|
||||
drawableLeft: Drawable,
|
||||
drawableMid: Drawable,
|
||||
drawableRight: Drawable
|
||||
) {
|
||||
|
||||
private val singleLineRenderer: TextRoundedBgRenderer by lazy {
|
||||
SingleLineRenderer(
|
||||
horizontalPadding = horizontalPadding,
|
||||
verticalPadding = verticalPadding,
|
||||
drawable = drawable
|
||||
)
|
||||
}
|
||||
|
||||
private val multiLineRenderer: TextRoundedBgRenderer by lazy {
|
||||
MultiLineRenderer(
|
||||
horizontalPadding = horizontalPadding,
|
||||
verticalPadding = verticalPadding,
|
||||
drawableLeft = drawableLeft,
|
||||
drawableMid = drawableMid,
|
||||
drawableRight = drawableRight
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function during onDraw of another widget such as TextView.
|
||||
*
|
||||
* @param canvas Canvas to draw onto
|
||||
* @param text
|
||||
* @param layout Layout that contains the text
|
||||
*/
|
||||
fun draw(canvas: Canvas, text: Spanned, layout: Layout) {
|
||||
// ideally the calculations here should be cached since they are not cheap. However, proper
|
||||
// invalidation of the cache is required whenever anything related to text has changed.
|
||||
val spans = text.getSpans(0, text.length, Annotation::class.java)
|
||||
spans.forEach { span ->
|
||||
if (span.value == VALUE_ROUNDED) {
|
||||
val spanStart = text.getSpanStart(span)
|
||||
val spanEnd = text.getSpanEnd(span)
|
||||
val startLine = layout.getLineForOffset(spanStart)
|
||||
val endLine = layout.getLineForOffset(spanEnd)
|
||||
|
||||
// start can be on the left or on the right depending on the language direction.
|
||||
val startOffset = (layout.getPrimaryHorizontal(spanStart)
|
||||
+ -1 * layout.getParagraphDirection(startLine) * horizontalPadding).toInt()
|
||||
// end can be on the left or on the right depending on the language direction.
|
||||
val endOffset = (layout.getPrimaryHorizontal(spanEnd)
|
||||
+ layout.getParagraphDirection(endLine) * horizontalPadding).toInt()
|
||||
|
||||
val renderer = if (startLine == endLine) singleLineRenderer else multiLineRenderer
|
||||
renderer.draw(canvas, layout, startLine, endLine, startOffset, endOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.agileburo.anytype.core_ui.widgets.text
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Layout
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Base class for single and multi line rounded background renderers.
|
||||
*
|
||||
* @param horizontalPadding the padding to be applied to left & right of the background
|
||||
* @param verticalPadding the padding to be applied to top & bottom of the background
|
||||
*/
|
||||
internal abstract class TextRoundedBgRenderer(
|
||||
val horizontalPadding: Int,
|
||||
val verticalPadding: Int
|
||||
) {
|
||||
|
||||
/**
|
||||
* Draw the background that starts at the {@code startOffset} and ends at {@code endOffset}.
|
||||
*
|
||||
* @param canvas Canvas to draw onto
|
||||
* @param layout Layout that contains the text
|
||||
* @param startLine the start line for the background
|
||||
* @param endLine the end line for the background
|
||||
* @param startOffset the character offset that the background should start at
|
||||
* @param endOffset the character offset that the background should end at
|
||||
*/
|
||||
abstract fun draw(
|
||||
canvas: Canvas,
|
||||
layout: Layout,
|
||||
startLine: Int,
|
||||
endLine: Int,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the top offset of the line and add padding into account so that there is a gap between
|
||||
* top of the background and top of the text.
|
||||
*
|
||||
* @param layout Layout object that contains the text
|
||||
* @param line line number
|
||||
*/
|
||||
protected fun getLineTop(layout: Layout, line: Int): Int {
|
||||
return layout.getLineTopWithoutPadding(line) - verticalPadding
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bottom offset of the line and add padding into account so that there is a gap between
|
||||
* bottom of the background and bottom of the text.
|
||||
*
|
||||
* @param layout Layout object that contains the text
|
||||
* @param line line number
|
||||
*/
|
||||
protected fun getLineBottom(layout: Layout, line: Int): Int {
|
||||
return layout.getLineBottomWithoutPadding(line) + verticalPadding
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the background for text that starts and ends on the same line.
|
||||
*
|
||||
* @param horizontalPadding the padding to be applied to left & right of the background
|
||||
* @param verticalPadding the padding to be applied to top & bottom of the background
|
||||
* @param drawable the drawable used to draw the background
|
||||
*/
|
||||
internal class SingleLineRenderer(
|
||||
horizontalPadding: Int,
|
||||
verticalPadding: Int,
|
||||
val drawable: Drawable
|
||||
) : TextRoundedBgRenderer(horizontalPadding, verticalPadding) {
|
||||
|
||||
override fun draw(
|
||||
canvas: Canvas,
|
||||
layout: Layout,
|
||||
startLine: Int,
|
||||
endLine: Int,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) {
|
||||
val lineTop = getLineTop(layout, startLine)
|
||||
val lineBottom = getLineBottom(layout, startLine)
|
||||
// get min of start/end for left, and max of start/end for right since we don't
|
||||
// the language direction
|
||||
val left = min(startOffset, endOffset)
|
||||
val right = max(startOffset, endOffset)
|
||||
drawable.setBounds(left, lineTop, right, lineBottom)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the background for text that starts and ends on different lines.
|
||||
*
|
||||
* @param horizontalPadding the padding to be applied to left & right of the background
|
||||
* @param verticalPadding the padding to be applied to top & bottom of the background
|
||||
* @param drawableLeft the drawable used to draw left edge of the background
|
||||
* @param drawableMid the drawable used to draw for whole line
|
||||
* @param drawableRight the drawable used to draw right edge of the background
|
||||
*/
|
||||
internal class MultiLineRenderer(
|
||||
horizontalPadding: Int,
|
||||
verticalPadding: Int,
|
||||
val drawableLeft: Drawable,
|
||||
val drawableMid: Drawable,
|
||||
val drawableRight: Drawable
|
||||
) : TextRoundedBgRenderer(horizontalPadding, verticalPadding) {
|
||||
|
||||
override fun draw(
|
||||
canvas: Canvas,
|
||||
layout: Layout,
|
||||
startLine: Int,
|
||||
endLine: Int,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) {
|
||||
// draw the first line
|
||||
val paragDir = layout.getParagraphDirection(startLine)
|
||||
val lineEndOffset = if (paragDir == Layout.DIR_RIGHT_TO_LEFT) {
|
||||
layout.getLineLeft(startLine) - horizontalPadding
|
||||
} else {
|
||||
layout.getLineRight(startLine) + horizontalPadding
|
||||
}.toInt()
|
||||
|
||||
var lineBottom = getLineBottom(layout, startLine)
|
||||
var lineTop = getLineTop(layout, startLine)
|
||||
drawStart(canvas, startOffset, lineTop, lineEndOffset, lineBottom)
|
||||
|
||||
// for the lines in the middle draw the mid drawable
|
||||
for (line in startLine + 1 until endLine) {
|
||||
lineTop = getLineTop(layout, line)
|
||||
lineBottom = getLineBottom(layout, line)
|
||||
drawableMid.setBounds(
|
||||
(layout.getLineLeft(line).toInt() - horizontalPadding),
|
||||
lineTop,
|
||||
(layout.getLineRight(line).toInt() + horizontalPadding),
|
||||
lineBottom
|
||||
)
|
||||
drawableMid.draw(canvas)
|
||||
}
|
||||
|
||||
val lineStartOffset = if (paragDir == Layout.DIR_RIGHT_TO_LEFT) {
|
||||
layout.getLineRight(startLine) + horizontalPadding
|
||||
} else {
|
||||
layout.getLineLeft(startLine) - horizontalPadding
|
||||
}.toInt()
|
||||
|
||||
// draw the last line
|
||||
lineBottom = getLineBottom(layout, endLine)
|
||||
lineTop = getLineTop(layout, endLine)
|
||||
|
||||
drawEnd(canvas, lineStartOffset, lineTop, endOffset, lineBottom)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the first line of a multiline annotation. Handles LTR/RTL.
|
||||
*
|
||||
* @param canvas Canvas to draw onto
|
||||
* @param start start coordinate for the background
|
||||
* @param top top coordinate for the background
|
||||
* @param end end coordinate for the background
|
||||
* @param bottom bottom coordinate for the background
|
||||
*/
|
||||
private fun drawStart(canvas: Canvas, start: Int, top: Int, end: Int, bottom: Int) {
|
||||
if (start > end) {
|
||||
drawableRight.setBounds(end, top, start, bottom)
|
||||
drawableRight.draw(canvas)
|
||||
} else {
|
||||
drawableLeft.setBounds(start, top, end, bottom)
|
||||
drawableLeft.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the last line of a multiline annotation. Handles LTR/RTL.
|
||||
*
|
||||
* @param canvas Canvas to draw onto
|
||||
* @param start start coordinate for the background
|
||||
* @param top top position for the background
|
||||
* @param end end coordinate for the background
|
||||
* @param bottom bottom coordinate for the background
|
||||
*/
|
||||
private fun drawEnd(canvas: Canvas, start: Int, top: Int, end: Int, bottom: Int) {
|
||||
if (start > end) {
|
||||
drawableLeft.setBounds(end, top, start, bottom)
|
||||
drawableLeft.draw(canvas)
|
||||
} else {
|
||||
drawableRight.setBounds(start, top, end, bottom)
|
||||
drawableRight.draw(canvas)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,11 +40,12 @@ class MarkupToolbarWidget : ConstraintLayout {
|
|||
LayoutInflater.from(context).inflate(R.layout.widget_markup_toolbar, this)
|
||||
}
|
||||
|
||||
fun markupClicks() = flowOf(bold(), italic(), strike(), link()).flattenMerge()
|
||||
fun markupClicks() = flowOf(bold(), italic(), strike(), link(), code()).flattenMerge()
|
||||
private fun bold() = bold.clicks().map { Markup.Type.BOLD }
|
||||
private fun italic() = italic.clicks().map { Markup.Type.ITALIC }
|
||||
private fun strike() = strike.clicks().map { Markup.Type.STRIKETHROUGH }
|
||||
private fun link() = link.clicks().map { Markup.Type.LINK }
|
||||
private fun code() = code.clicks().map { Markup.Type.KEYBOARD }
|
||||
|
||||
fun colorClicks() = color.clicks()
|
||||
fun hideKeyboardClicks() = keyboard.clicks()
|
||||
|
|
9
core-ui/src/main/res/drawable/rounded_text_bg.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/markup_keyboard_bg" />
|
||||
<solid android:color="@color/markup_keyboard_bg" />
|
||||
</shape>
|
10
core-ui/src/main/res/drawable/rounded_text_bg_left.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/roundedTextBorderRadius"
|
||||
android:topLeftRadius="@dimen/roundedTextBorderRadius" />
|
||||
<stroke
|
||||
android:width="@dimen/roundedTextBorderWidth"
|
||||
android:color="@color/markup_keyboard_bg" />
|
||||
<solid android:color="@color/markup_keyboard_bg" />
|
||||
</shape>
|
8
core-ui/src/main/res/drawable/rounded_text_bg_mid.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/markup_keyboard_bg" />
|
||||
<solid android:color="@color/markup_keyboard_bg" />
|
||||
</shape>
|
11
core-ui/src/main/res/drawable/rounded_text_bg_right.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners
|
||||
android:bottomRightRadius="@dimen/roundedTextBorderRadius"
|
||||
android:topRightRadius="@dimen/roundedTextBorderRadius" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/markup_keyboard_bg" />
|
||||
<solid android:color="@color/markup_keyboard_bg" />
|
||||
</shape>
|
|
@ -6,4 +6,13 @@
|
|||
<declare-styleable name="ToolbarSectionWidget">
|
||||
<attr name="active_background_color" format="color" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="TextRoundedBgHelper">
|
||||
<attr name="roundedTextHorizontalPadding" format="dimension" />
|
||||
<attr name="roundedTextVerticalPadding" format="dimension" />
|
||||
<attr name="roundedTextDrawable" format="reference" />
|
||||
<attr name="roundedTextDrawableLeft" format="reference" />
|
||||
<attr name="roundedTextDrawableMid" format="reference" />
|
||||
<attr name="roundedTextDrawableRight" format="reference" />
|
||||
</declare-styleable>
|
||||
</resources>
|
|
@ -24,6 +24,8 @@
|
|||
<color name="kanban_second">#BDBDBD</color>
|
||||
|
||||
<color name="divider">#ACA996</color>
|
||||
|
||||
<color name="markup_keyboard_bg">#F3F2EC</color>
|
||||
|
||||
<array name="toolbar_color_text_colors">
|
||||
<item>#2C2B27</item>
|
||||
|
|
|
@ -75,4 +75,7 @@
|
|||
<dimen name="dp_6">6dp</dimen>
|
||||
<dimen name="dp_8">8dp</dimen>
|
||||
|
||||
<dimen name="roundedTextBorderRadius">4dp</dimen>
|
||||
<dimen name="roundedTextBorderWidth">1dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -212,4 +212,13 @@
|
|||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="RoundedBgTextView" parent="@android:style/Widget.TextView">
|
||||
<item name="roundedTextHorizontalPadding">2dp</item>
|
||||
<item name="roundedTextVerticalPadding">2dp</item>
|
||||
<item name="roundedTextDrawable">@drawable/rounded_text_bg</item>
|
||||
<item name="roundedTextDrawableLeft">@drawable/rounded_text_bg_left</item>
|
||||
<item name="roundedTextDrawableMid">@drawable/rounded_text_bg_mid</item>
|
||||
<item name="roundedTextDrawableRight">@drawable/rounded_text_bg_right</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -65,13 +65,20 @@ fun BlockEntity.Content.Text.Mark.toMiddleware(): Block.Content.Text.Mark {
|
|||
.build()
|
||||
}
|
||||
BlockEntity.Content.Text.Mark.Type.BACKGROUND_COLOR -> {
|
||||
Models.Block.Content.Text.Mark
|
||||
Block.Content.Text.Mark
|
||||
.newBuilder()
|
||||
.setType(Models.Block.Content.Text.Mark.Type.BackgroundColor)
|
||||
.setType(Block.Content.Text.Mark.Type.BackgroundColor)
|
||||
.setRange(rangeModel)
|
||||
.setParam(param as String)
|
||||
.build()
|
||||
}
|
||||
BlockEntity.Content.Text.Mark.Type.KEYBOARD -> {
|
||||
Block.Content.Text.Mark
|
||||
.newBuilder()
|
||||
.setType(Block.Content.Text.Mark.Type.Keyboard)
|
||||
.setRange(rangeModel)
|
||||
.build()
|
||||
}
|
||||
else -> throw IllegalStateException("Unsupported mark type: ${type.name}")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,13 @@ private fun mapMarks(content: Block.Content.Text): List<Markup.Mark> =
|
|||
param = checkNotNull(mark.param)
|
||||
)
|
||||
}
|
||||
Block.Content.Text.Mark.Type.KEYBOARD -> {
|
||||
Markup.Mark(
|
||||
from = mark.range.first,
|
||||
to = mark.range.last,
|
||||
type = Markup.Type.KEYBOARD
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,6 +235,7 @@ class PageViewModel(
|
|||
Markup.Type.TEXT_COLOR -> Block.Content.Text.Mark.Type.TEXT_COLOR
|
||||
Markup.Type.LINK -> Block.Content.Text.Mark.Type.LINK
|
||||
Markup.Type.BACKGROUND_COLOR -> Block.Content.Text.Mark.Type.BACKGROUND_COLOR
|
||||
Markup.Type.KEYBOARD -> Block.Content.Text.Mark.Type.KEYBOARD
|
||||
},
|
||||
param = action.param
|
||||
)
|
||||
|
@ -287,11 +288,11 @@ class PageViewModel(
|
|||
Timber.d("Preparing views for focus: $focus")
|
||||
|
||||
models.asMap().asRender(pageId).mapNotNull { block ->
|
||||
when {
|
||||
block.content is Block.Content.Text -> {
|
||||
when (block.content) {
|
||||
is Block.Content.Text -> {
|
||||
block.toView(focused = block.id == focus)
|
||||
}
|
||||
block.content is Block.Content.Image -> {
|
||||
is Block.Content.Image -> {
|
||||
block.toView()
|
||||
}
|
||||
else -> null
|
||||
|
|
1
sample/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
43
sample/build.gradle
Normal file
|
@ -0,0 +1,43 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.agileburo.anytype.sample"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(':core-utils')
|
||||
implementation project(':core-ui')
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.core:core-ktx:1.0.2'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||
}
|
21
sample/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,24 @@
|
|||
package com.agileburo.anytype.sample
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.agileburo.anytype.sample", appContext.packageName)
|
||||
}
|
||||
}
|
21
sample/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.agileburo.anytype.sample">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,90 @@
|
|||
package com.agileburo.anytype.sample
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Annotation
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.item_test.view.*
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val annotation = Annotation("key", "rounded")
|
||||
val list = mutableListOf<Item>()
|
||||
|
||||
val range = IntRange(0, 50)
|
||||
range.forEach {
|
||||
|
||||
var t = "I am block number $it"
|
||||
val spannable = SpannableString(t)
|
||||
if (it == 10) {
|
||||
spannable.setSpan(annotation, 0, t.length, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
|
||||
list.add(Item(text = spannable))
|
||||
} else {
|
||||
list.add(Item(text = spannable))
|
||||
}
|
||||
}
|
||||
|
||||
with(recyclerView) {
|
||||
layoutManager = LinearLayoutManager(this@MainActivity)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
this@MainActivity,
|
||||
DividerItemDecoration.VERTICAL
|
||||
)
|
||||
)
|
||||
adapter = MarkupAdapter(list) { pos: Int ->
|
||||
(this.adapter as MarkupAdapter).setData(
|
||||
Item(SpannableString("I am block number $pos")),
|
||||
pos
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MarkupAdapter(private val data: MutableList<Item>, private val listener: (Int) -> Unit) :
|
||||
RecyclerView.Adapter<MarkupAdapter.MarkupViewHolder>() {
|
||||
|
||||
fun setData(item: Item, position: Int) {
|
||||
data[position] = item
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MarkupViewHolder {
|
||||
val view =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.item_test, parent, false)
|
||||
return MarkupViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
override fun onBindViewHolder(holder: MarkupViewHolder, position: Int) {
|
||||
holder.bind(data[position].text, listener)
|
||||
}
|
||||
|
||||
class MarkupViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(text: SpannableString, listener: (Int) -> Unit) {
|
||||
itemView.item.text = text
|
||||
itemView.setOnClickListener {
|
||||
listener.invoke(adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Item(val text: SpannableString)
|
|
@ -0,0 +1,42 @@
|
|||
package com.agileburo.anytype.sample
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.text.Spanned
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.graphics.withTranslation
|
||||
import com.agileburo.anytype.core_ui.widgets.text.TextRoundedBgAttributeReader
|
||||
import com.agileburo.anytype.core_ui.widgets.text.TextRoundedBgHelper
|
||||
|
||||
class RoundedBgTextView : AppCompatTextView {
|
||||
|
||||
private val textRoundedBgHelper: TextRoundedBgHelper
|
||||
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = android.R.attr.textViewStyle
|
||||
) : super(context, attrs, defStyleAttr) {
|
||||
val attributeReader = TextRoundedBgAttributeReader(context, attrs)
|
||||
textRoundedBgHelper = TextRoundedBgHelper(
|
||||
horizontalPadding = attributeReader.horizontalPadding,
|
||||
verticalPadding = attributeReader.verticalPadding,
|
||||
drawable = attributeReader.drawable,
|
||||
drawableLeft = attributeReader.drawableLeft,
|
||||
drawableMid = attributeReader.drawableMid,
|
||||
drawableRight = attributeReader.drawableRight
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
// need to draw bg first so that text can be on top during super.onDraw()
|
||||
if (text is Spanned && layout != null) {
|
||||
canvas.withTranslation(totalPaddingLeft.toFloat(), totalPaddingTop.toFloat()) {
|
||||
textRoundedBgHelper.draw(canvas, text as Spanned, layout)
|
||||
}
|
||||
}
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
}
|
34
sample/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
170
sample/src/main/res/drawable/ic_launcher_background.xml
Normal file
|
@ -0,0 +1,170 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
15
sample/src/main/res/layout/activity_main.xml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
14
sample/src/main/res/layout/item_test.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp">
|
||||
|
||||
<com.agileburo.anytype.sample.RoundedBgTextView
|
||||
android:id="@+id/item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textSize="21sp"
|
||||
android:textColor="@android:color/black"
|
||||
android:gravity="center"/>
|
||||
|
||||
</FrameLayout>
|
5
sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
BIN
sample/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
sample/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
sample/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 15 KiB |
6
sample/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
</resources>
|
3
sample/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Sample</string>
|
||||
</resources>
|
11
sample/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,17 @@
|
|||
package com.agileburo.anytype.sample
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
include ':app',
|
||||
':sample',
|
||||
':core-utils',
|
||||
':middleware',
|
||||
':protobuf',
|
||||
|
|