Archived
Initial push; upped AGP to 8.0.1, Kotlin plugin to 1.8.20, as well as updated all dependencies.
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
package com.denizd.textbasic.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import android.util.Log
|
||||
import com.denizd.textbasic.QuoteStorage
|
||||
import com.denizd.textbasic.R
|
||||
|
||||
class CanvasText {
|
||||
|
||||
companion object {
|
||||
fun drawText(context: Context, isPreview: Boolean = false, size: Pair<Int, Int>? = null): Bitmap {
|
||||
|
||||
|
||||
val previewSize = 20f
|
||||
|
||||
val storage = QuoteStorage(context)
|
||||
|
||||
size?.let {
|
||||
storage.setSize(size)
|
||||
// Log.d("ASDF", "${size.first} + ${size.second}")
|
||||
}
|
||||
|
||||
val widgetSize = storage.getSize()
|
||||
val outlineSize = storage.getOutlineSize()
|
||||
|
||||
val bmp = if (isPreview) {
|
||||
Bitmap.createBitmap(1024, 256, Bitmap.Config.ARGB_8888)
|
||||
} else {
|
||||
// Log.d("ASDFasdf", "${widgetSize.first} + ${widgetSize.second}")
|
||||
Bitmap.createBitmap(widgetSize.first * 3, widgetSize.second * 3, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
|
||||
val canvas = Canvas(bmp)
|
||||
|
||||
val quote = if (isPreview) {
|
||||
context.getString(R.string.example_text)
|
||||
} else if (storage.isOrderRandom()) {
|
||||
storage.getRandomQuote()
|
||||
} else {
|
||||
storage.getNextQuote()
|
||||
}.ifBlank { context.getString(R.string.info_add_text) }
|
||||
|
||||
val colours = storage.getColours(context = context)
|
||||
|
||||
val scaledTextSize =
|
||||
context.resources.displayMetrics.scaledDensity * if (/*isPreview*/false) previewSize else {
|
||||
storage.getTextSize().toFloat()
|
||||
}
|
||||
val savedTypeface = storage.getTypeface()
|
||||
|
||||
val widgetGravity = storage.getWidgetGravity()
|
||||
|
||||
val paint = TextPaint().apply {
|
||||
style = Paint.Style.FILL
|
||||
color = colours.first
|
||||
textSize = scaledTextSize
|
||||
textAlign = when (widgetGravity) {
|
||||
0, 3, 6 -> Paint.Align.LEFT
|
||||
2, 5, 8 -> Paint.Align.RIGHT
|
||||
else -> Paint.Align.CENTER
|
||||
}
|
||||
|
||||
isAntiAlias = true
|
||||
isSubpixelText = false
|
||||
typeface = savedTypeface
|
||||
}
|
||||
|
||||
val staticLayout = StaticLayout.Builder
|
||||
.obtain(quote, 0, quote.length, paint, if (isPreview) 1024 else widgetSize.first * 3).build()
|
||||
|
||||
val boundsText = Rect()
|
||||
paint.getTextBounds(quote, 0, quote.length, boundsText)
|
||||
val (x, y) = calculateWidgetGravity(storage.getWidgetGravity(), bmp, boundsText, isPreview)
|
||||
// val x = canvas.width / 2
|
||||
// val y = ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2))
|
||||
|
||||
val padding = 16f
|
||||
val alignment = when (widgetGravity) {
|
||||
0 -> {
|
||||
canvas.translate(boundsText.left.toFloat() + padding, 0f)
|
||||
Paint.Align.LEFT
|
||||
}
|
||||
1 -> {
|
||||
canvas.translate(canvas.width / 2f, 0f)
|
||||
Paint.Align.CENTER
|
||||
}
|
||||
2 -> {
|
||||
canvas.translate(canvas.width.toFloat() - padding, 0f)
|
||||
Paint.Align.RIGHT
|
||||
}
|
||||
3 -> {
|
||||
canvas.translate(boundsText.left.toFloat() + padding, 0f)
|
||||
Paint.Align.LEFT
|
||||
}
|
||||
5 -> {
|
||||
canvas.translate(canvas.width.toFloat() - padding, 0f)
|
||||
Paint.Align.RIGHT
|
||||
}
|
||||
6 -> {
|
||||
canvas.translate(boundsText.left.toFloat() + padding, bmp.height.toFloat() - staticLayout.height - padding)
|
||||
Paint.Align.LEFT
|
||||
}
|
||||
7 -> {
|
||||
canvas.translate(canvas.width / 2f, bmp.height.toFloat() - staticLayout.height - padding)
|
||||
Paint.Align.CENTER
|
||||
}
|
||||
8 -> {
|
||||
canvas.translate(canvas.width.toFloat() - padding, bmp.height.toFloat() - staticLayout.height - padding)
|
||||
Paint.Align.RIGHT
|
||||
}
|
||||
else -> { // 4
|
||||
canvas.translate(canvas.width / 2f, 0f)
|
||||
Paint.Align.CENTER
|
||||
}
|
||||
}
|
||||
|
||||
when (val bgType = storage.getBackgroundType()) {
|
||||
0 -> { // background
|
||||
val bgPaint = Paint().apply {
|
||||
style = Paint.Style.FILL
|
||||
color = colours.second
|
||||
}
|
||||
val rounding = 24f
|
||||
|
||||
// val rect = roundedRect(
|
||||
// boundsText.left + x - padding,
|
||||
// boundsText.top + y - padding,
|
||||
// boundsText.right + x + padding,
|
||||
// boundsText.bottom + y + padding, rounding, rounding
|
||||
// )
|
||||
|
||||
// val rect = roundedRect(
|
||||
// 0f, 0f, boundsText.right.toFloat() / 2, staticLayout.height.toFloat(),
|
||||
// rounding, rounding
|
||||
// )
|
||||
val rect = when (widgetGravity) { // right
|
||||
2, 5, 8 -> roundedRect(
|
||||
-boundsText.right.toFloat() - 4f, 0f, 0f, staticLayout.height.toFloat(),
|
||||
rounding, rounding
|
||||
)
|
||||
1, 4, 7 -> roundedRect( // middle
|
||||
-(boundsText.width() / 2f), 0f,
|
||||
boundsText.right.toFloat() / 2f, staticLayout.height.toFloat(),
|
||||
rounding, rounding
|
||||
)
|
||||
else -> roundedRect( // left
|
||||
0f, 0f, boundsText.right.toFloat() + 4f, staticLayout.height.toFloat(),
|
||||
rounding, rounding
|
||||
)
|
||||
}
|
||||
|
||||
canvas.drawPath(rect, bgPaint)
|
||||
}
|
||||
1, 2 -> { // outline or shadow
|
||||
val strokePaint = TextPaint().apply {
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = outlineSize //if (isPreview) previewSize / 1.5f else
|
||||
color = colours.second
|
||||
textSize = scaledTextSize
|
||||
textAlign = alignment
|
||||
|
||||
isAntiAlias = true
|
||||
isSubpixelText = false
|
||||
typeface = savedTypeface
|
||||
|
||||
if (bgType == 2) { // blur outline for shadow
|
||||
maskFilter = BlurMaskFilter(if (outlineSize == 0f) 1f else outlineSize, BlurMaskFilter.Blur.NORMAL)
|
||||
}
|
||||
}
|
||||
val strokeStaticLayout = StaticLayout.Builder
|
||||
.obtain(quote, 0, quote.length, strokePaint, if (isPreview) 1024 else widgetSize.first * 3).build()
|
||||
strokeStaticLayout.draw(canvas)
|
||||
// canvas.drawText(quote, x, y, strokePaint)
|
||||
}
|
||||
}
|
||||
|
||||
staticLayout.draw(canvas)
|
||||
// canvas.drawText(quote, x, y, paint)
|
||||
return bmp
|
||||
}
|
||||
|
||||
private fun calculateWidgetGravity(widgetGravity: Int, bitmap: Bitmap, boundsText: Rect, isPreview: Boolean) : Pair<Float, Float> {
|
||||
return if (isPreview) {
|
||||
Pair((bitmap.width - boundsText.width()) / 2f, (bitmap.height + boundsText.height()) / 2f)
|
||||
} else when (widgetGravity) {
|
||||
0 -> { // top left
|
||||
Pair(0f + boundsText.left, (boundsText.height() - boundsText.top).toFloat())
|
||||
}
|
||||
1 -> { // top center
|
||||
Pair(
|
||||
(bitmap.width - boundsText.width()) / 2f,
|
||||
(boundsText.height() - boundsText.top).toFloat()
|
||||
)
|
||||
}
|
||||
2 -> { // top right
|
||||
Pair(
|
||||
(bitmap.width - (boundsText.right)).toFloat(),
|
||||
(boundsText.height() - boundsText.top).toFloat()
|
||||
)
|
||||
}
|
||||
3 -> { // center left
|
||||
Pair(0f + boundsText.left, (bitmap.height + boundsText.height()) / 2f)
|
||||
}
|
||||
// 4 == else branch
|
||||
5 -> { // center right
|
||||
Pair(
|
||||
(bitmap.width - (boundsText.right)).toFloat(),
|
||||
(bitmap.height + boundsText.height()) / 2f
|
||||
)
|
||||
}
|
||||
6 -> { // bottom left
|
||||
Pair(0f + boundsText.left, (bitmap.height - boundsText.bottom).toFloat())
|
||||
}
|
||||
7 -> { // bottom center
|
||||
Pair(
|
||||
(bitmap.width - boundsText.width()) / 2f,
|
||||
(bitmap.height - boundsText.bottom).toFloat()
|
||||
)
|
||||
}
|
||||
8 -> { // bottom right
|
||||
Pair(
|
||||
(bitmap.width - (boundsText.right)).toFloat(),
|
||||
(bitmap.height - boundsText.bottom).toFloat()
|
||||
)
|
||||
}
|
||||
else -> { // center center LIKE 4
|
||||
Pair(
|
||||
(bitmap.width - boundsText.width()) / 2f,
|
||||
(bitmap.height + boundsText.height()) / 2f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun roundedRect(
|
||||
left: Float, top: Float, right: Float, bottom: Float,
|
||||
roundingX: Float, roundingY: Float
|
||||
): Path {
|
||||
var rx = roundingX
|
||||
var ry = roundingY
|
||||
val path = Path()
|
||||
if (rx < 0) rx = 0f
|
||||
if (ry < 0) ry = 0f
|
||||
val width = right - left
|
||||
val height = bottom - top
|
||||
if (rx > width / 2) rx = width / 2
|
||||
if (ry > height / 2) ry = height / 2
|
||||
val widthMinusCorners = width - 2 * rx
|
||||
val heightMinusCorners = height - 2 * ry
|
||||
path.moveTo(right, top + ry)
|
||||
path.rQuadTo(0f, -ry, -rx, -ry) //top-right corner
|
||||
path.rLineTo(-widthMinusCorners, 0f)
|
||||
path.rQuadTo(-rx, 0f, -rx, ry) //top-left corner
|
||||
path.rLineTo(0f, heightMinusCorners)
|
||||
|
||||
path.rQuadTo(0f, ry, rx, ry) //bottom-left corner
|
||||
path.rLineTo(widthMinusCorners, 0f)
|
||||
path.rQuadTo(rx, 0f, rx, -ry) //bottom-right corner
|
||||
|
||||
path.rLineTo(0f, -heightMinusCorners)
|
||||
path.close() //Given close, last lineto can be removed.
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.denizd.textbasic.widget
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.*
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.RemoteViews
|
||||
import com.denizd.textbasic.R
|
||||
import kotlin.random.Random
|
||||
|
||||
class TextCanvasWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
|
||||
// Perform this loop procedure for each App Widget that belongs to this provider
|
||||
appWidgetIds.forEach { appWidgetId ->
|
||||
|
||||
val remoteViews = configureViews(context, appWidgetIds)
|
||||
// Tell the AppWidgetManager to perform an update on the current app widget
|
||||
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureViews(context: Context, appWidgetIds: IntArray, size: Pair<Int, Int>? = null): RemoteViews =
|
||||
RemoteViews(context.packageName, R.layout.widget_bitmap).apply {
|
||||
|
||||
val bitmap = CanvasText.drawText(context, size = size)
|
||||
|
||||
setImageViewBitmap(R.id.bitmap_view, bitmap)
|
||||
|
||||
setOnClickPendingIntent(
|
||||
R.id.bmp_root_layout,
|
||||
Intent(context, TextCanvasWidget::class.java).let { intent ->
|
||||
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
|
||||
PendingIntent.getBroadcast(
|
||||
context, Random.nextInt(), intent, PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAppWidgetOptionsChanged(
|
||||
context: Context?,
|
||||
appWidgetManager: AppWidgetManager?,
|
||||
appWidgetId: Int,
|
||||
newOptions: Bundle?
|
||||
) {
|
||||
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
|
||||
|
||||
val width = (newOptions?.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) ?: 0)
|
||||
val height = (newOptions?.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) ?: 0)
|
||||
|
||||
// Log.d("ASDFF", (newOptions?.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)).toString())
|
||||
|
||||
|
||||
context?.let {
|
||||
val remoteViews = configureViews(context, intArrayOf(appWidgetId), Pair(width, height))
|
||||
// Tell the AppWidgetManager to perform an update on the current app widget
|
||||
appWidgetManager?.updateAppWidget(appWidgetId, remoteViews)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user