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,55 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.R
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Created by Hemant chand on 05/07/17.
|
||||
*/
|
||||
object ColorTransparentUtils {
|
||||
// This default color int
|
||||
const val defaultColorID = R.color.black
|
||||
const val defaultColor = "000000"
|
||||
const val TAG = "ColorTransparentUtils"
|
||||
|
||||
/**
|
||||
* This method convert numver into hexa number or we can say transparent code
|
||||
*
|
||||
* @param trans number of transparency you want
|
||||
* @return it return hex decimal number or transparency code
|
||||
*/
|
||||
fun convert(trans: Int): String {
|
||||
val hexString = Integer.toHexString((255 * trans / 100.0).roundToInt())
|
||||
return (if (hexString.length < 2) "0" else "") + hexString
|
||||
}
|
||||
|
||||
fun transparentColor(colorCode: Int, trans: Int): String {
|
||||
return convertIntoColor(colorCode, trans)
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert color code into transparent color code
|
||||
*
|
||||
* @param colorCode color code
|
||||
* @param transCode transparent number
|
||||
* @return transparent color code
|
||||
*/
|
||||
fun convertIntoColor(colorCode: Int, transCode: Int): String {
|
||||
// convert color code into hexa string and remove starting 2 digit
|
||||
var color = defaultColor
|
||||
try {
|
||||
color = Integer.toHexString(colorCode).uppercase(Locale.getDefault()).substring(2)
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
return if (color.isNotEmpty() && transCode < 101) {
|
||||
if (color.trim { it <= ' ' }.length == 6) {
|
||||
"#" + convert(transCode) + color
|
||||
} else {
|
||||
// Log.d(TAG, "Color is already with transparency")
|
||||
convert(transCode) + color
|
||||
}
|
||||
} else "#" + Integer.toHexString(defaultColorID).uppercase(Locale.getDefault()).substring(2)
|
||||
// if color is empty or any other problem occur then we return deafult color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.app.backup.*
|
||||
|
||||
class DriveAgent : BackupAgentHelper() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
SharedPreferencesBackupHelper(this, QuoteStorage.PREF_NAME).also {
|
||||
addHelper(QuoteStorage.PREF_BACKUP_KEY, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.app.backup.BackupManager
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.denizd.textbasic.databinding.ActivityMainBinding
|
||||
import com.denizd.textbasic.fragment.GuideFragment
|
||||
import com.denizd.textbasic.fragment.QuoteFragment
|
||||
import com.denizd.textbasic.fragment.SettingsFragment
|
||||
import com.denizd.textbasic.widget.TextCanvasWidget
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityMainBinding.inflate(this.layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
// binding.buttonBackupDownload.setOnClickListener {
|
||||
//
|
||||
// Snackbar.make(binding.rootLayout, R.string.snack_backup_download, Snackbar.LENGTH_LONG).show()
|
||||
// }
|
||||
|
||||
binding.buttonBackupUpload.setOnClickListener {
|
||||
BackupManager(this).dataChanged()
|
||||
Snackbar.make(binding.rootLayout, R.string.snack_backup_upload, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
binding.bottomNav.setOnItemSelectedListener { item ->
|
||||
loadFragment(item.itemId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (supportFragmentManager.fragments.size == 0) {
|
||||
binding.bottomNav.selectedItemId = R.id.quotes
|
||||
loadFragment(R.id.quotes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFragment(type: Int): Boolean {
|
||||
val fragment = when (type) {
|
||||
R.id.quotes -> QuoteFragment()
|
||||
R.id.guide -> GuideFragment()
|
||||
R.id.settings -> SettingsFragment()
|
||||
else -> QuoteFragment()
|
||||
}//supportFragmentManager.findFragmentByTag(type) ?: viewModel.getFragment(type)
|
||||
|
||||
if (supportFragmentManager.fragments.isNotEmpty() &&
|
||||
supportFragmentManager.fragments[supportFragmentManager.fragments.size-1]
|
||||
.tag?.equals(type.toString()) == true
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (fragment.lifecycle.currentState != Lifecycle.State.INITIALIZED) {
|
||||
return false
|
||||
}
|
||||
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, fragment, type.toString())
|
||||
// .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.commit()
|
||||
|
||||
// with (supportFragmentManager) {
|
||||
// beginTransaction()
|
||||
// .hide(fragments[fragments.size - 1]) // should this be 0?
|
||||
// .show(findFragmentByTag(type) ?: TODO())
|
||||
// .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
// .commit()
|
||||
// }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
||||
arrayOf(TextCanvasWidget::class.java).forEach { widget ->
|
||||
sendBroadcast(Intent(this, widget).also { intent ->
|
||||
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, AppWidgetManager.getInstance(this)
|
||||
.getAppWidgetIds(ComponentName(application, widget)))
|
||||
})
|
||||
}
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
data class Quote(val id: Int, var text: String)
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
||||
class QuoteAdapter(quotes: List<String>) : RecyclerView.Adapter<QuoteAdapter.QuoteViewHolder>() {
|
||||
|
||||
private var mutableQuotes: MutableList<String> = quotes.toMutableList()
|
||||
|
||||
class QuoteViewHolder(view: View) : RecyclerView.ViewHolder(view)/*, View.OnClickListener, View.OnLongClickListener*/ {
|
||||
|
||||
val quote: TextView = view.findViewById(R.id.quote)
|
||||
val delete: ImageButton = view.findViewById(R.id.delete_button)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return /*if (position == itemCount - 1) 1 else*/ 0
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
|
||||
val v = LayoutInflater.from(parent.context).inflate(R.layout.quote_entry, parent, false)
|
||||
return QuoteViewHolder(v)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
|
||||
val currentItem = mutableQuotes[position]
|
||||
|
||||
holder.quote.text = currentItem
|
||||
|
||||
holder.quote.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
override fun afterTextChanged(p0: Editable?) {}
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
mutableQuotes[holder.layoutPosition] = p0.toString()
|
||||
}
|
||||
})
|
||||
|
||||
holder.delete.setOnClickListener {
|
||||
mutableQuotes.removeAt(holder.layoutPosition)
|
||||
notifyItemRemoved(holder.layoutPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = mutableQuotes.size
|
||||
|
||||
fun setQuotes(quotes: List<String>) {
|
||||
this.mutableQuotes = quotes.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun addNewQuote() {
|
||||
mutableQuotes.add("")
|
||||
notifyItemInserted(mutableQuotes.size + 1)
|
||||
}
|
||||
|
||||
fun getAllQuotes(): Array<String> {
|
||||
return mutableQuotes.filter { s -> s.isNotBlank() }.toTypedArray()
|
||||
}
|
||||
|
||||
interface QuoteClickListener {
|
||||
fun onQuoteClick(quotes: String)
|
||||
fun onQuoteLongClick(quoteId: Int)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.util.Log
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
class QuoteStorage(context: Context) {
|
||||
|
||||
companion object {
|
||||
|
||||
const val PREF_NAME = "textbasicprefs"
|
||||
const val PREF_BACKUP_KEY = "textbasicprefskey"
|
||||
const val SEPARATOR = '\uFFFF'
|
||||
|
||||
private const val KEY_QUOTES = "quotes"
|
||||
private const val KEY_QUOTE_COUNTER = "quotecounter"
|
||||
private const val KEY_TEXT_SIZE = "textsize"
|
||||
private const val KEY_INVERTED = "inverted"
|
||||
private const val KEY_HIGH_CONTRAST = "hicontrast"
|
||||
private const val KEY_TRANSPARENCY = "transparency"
|
||||
private const val KEY_RANDOM = "random"
|
||||
private const val KEY_WIDGET_POSITION = "widgetposition"
|
||||
private const val KEY_BACKGROUND_TYPE = "backgroundtype"
|
||||
private const val KEY_TYPEFACE = "typeface"
|
||||
private const val KEY_TYPEFACE_STYLE = "typefacestyle"
|
||||
private const val KEY_AVG_WIDTH = "avgwidth"
|
||||
private const val KEY_AVG_HEIGHT = "avgheight"
|
||||
private const val KEY_OUTLINE_SIZE = "outlinesize"
|
||||
|
||||
val prefGroups = arrayOf(
|
||||
KEY_QUOTES, KEY_QUOTE_COUNTER, KEY_TEXT_SIZE, KEY_INVERTED, KEY_HIGH_CONTRAST,
|
||||
KEY_TRANSPARENCY, KEY_RANDOM, KEY_WIDGET_POSITION
|
||||
)
|
||||
|
||||
|
||||
// val transparencyValues = arrayOf(
|
||||
// "0", "5", "10", "15", "20", "25", "30", "35", "40", "45", "50",
|
||||
// "55", "60", "65", "70", "75", "80", "85", "90", "95", "100",
|
||||
// )
|
||||
}
|
||||
|
||||
private val prefs = context.getSharedPreferences(PREF_NAME, 0)
|
||||
|
||||
fun getAllQuotes(): List<String> = ((prefs.getString(KEY_QUOTES, "")) ?: "").split(SEPARATOR).toList()
|
||||
|
||||
fun getRandomQuote(): String {
|
||||
val quotes = getAllQuotes()
|
||||
return quotes.random()
|
||||
}
|
||||
|
||||
fun getNextQuote(): String {
|
||||
val currentQuoteCounter: Int = prefs.getInt(KEY_QUOTE_COUNTER, 0)
|
||||
val quotes = getAllQuotes()
|
||||
val nextQuoteCounter: Int = if (currentQuoteCounter >= quotes.size - 1) 0 else currentQuoteCounter + 1
|
||||
prefs.edit().putInt(
|
||||
KEY_QUOTE_COUNTER,
|
||||
nextQuoteCounter
|
||||
).apply()
|
||||
return quotes[nextQuoteCounter]
|
||||
}
|
||||
|
||||
fun getTextSize() = prefs.getInt(KEY_TEXT_SIZE, 18)
|
||||
|
||||
fun isInvertedEnabled(): Boolean = prefs.getBoolean(KEY_INVERTED, false)
|
||||
fun isHighContrastEnabled(): Boolean = prefs.getBoolean(KEY_HIGH_CONTRAST, false)
|
||||
fun getTransparency(): Int = prefs.getInt(KEY_TRANSPARENCY, 100)
|
||||
fun isOrderRandom(): Boolean = prefs.getBoolean(KEY_RANDOM, false)
|
||||
fun getWidgetGravity(): Int = prefs.getInt(KEY_WIDGET_POSITION, 1)
|
||||
fun getBackgroundType(): Int = prefs.getInt(KEY_BACKGROUND_TYPE, 0)
|
||||
fun getTypefaceIndex(): Int = prefs.getInt(KEY_TYPEFACE, 0)
|
||||
fun getTypeface(): Typeface = Typeface.create(
|
||||
when (getTypefaceIndex()) {
|
||||
1 -> Typeface.SERIF
|
||||
2 -> Typeface.MONOSPACE
|
||||
else -> Typeface.SANS_SERIF
|
||||
},
|
||||
getTypefaceStyle()
|
||||
)
|
||||
fun getTypefaceStyle(): Int = prefs.getInt(KEY_TYPEFACE_STYLE, 0)
|
||||
fun getOutlineSize(): Float = prefs.getFloat(KEY_OUTLINE_SIZE, 8f)
|
||||
|
||||
// Pair<text colour, background colour>
|
||||
fun getColours(
|
||||
isInverted: Boolean = isInvertedEnabled(),
|
||||
transparency: Int = getTransparency(),
|
||||
context: Context
|
||||
): Pair<Int, Int> {
|
||||
val colours = when (isInverted) {
|
||||
true -> Pair(R.color.widget_dark, R.color.widget_light)
|
||||
false -> Pair(R.color.widget_light, R.color.widget_dark)
|
||||
}
|
||||
|
||||
return Pair(
|
||||
context.getColor(colours.first),//Color.parseColor(ColorTransparentUtils.transparentColor(colours.first, 10)),
|
||||
Color.parseColor(ColorTransparentUtils.transparentColor(context.getColor(colours.second), transparency * 5))
|
||||
)
|
||||
}
|
||||
|
||||
fun setSize(size: Pair<Int, Int>) {
|
||||
prefs.edit().apply {
|
||||
putInt(KEY_AVG_WIDTH, size.first)
|
||||
putInt(KEY_AVG_HEIGHT, size.second)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun getSize(): Pair<Int, Int> = Pair(prefs.getInt(KEY_AVG_WIDTH, 1), prefs.getInt(KEY_AVG_HEIGHT, 1))
|
||||
|
||||
fun saveSettings(
|
||||
textSize: Int, inverted: Boolean, transparency: Int, random: Boolean,
|
||||
widgetPosition: Int, backgroundType: Int, typefaceIndex: Int, typefaceStyleIndex: Int,
|
||||
outlineSize: Float
|
||||
) {
|
||||
prefs.edit().apply {
|
||||
putInt(KEY_TEXT_SIZE, textSize)
|
||||
putBoolean(KEY_INVERTED, inverted)
|
||||
putInt(KEY_TRANSPARENCY, transparency)
|
||||
putBoolean(KEY_RANDOM, random)
|
||||
putInt(KEY_WIDGET_POSITION, widgetPosition)
|
||||
putInt(KEY_BACKGROUND_TYPE, backgroundType)
|
||||
putInt(KEY_TYPEFACE, typefaceIndex)
|
||||
putInt(KEY_TYPEFACE_STYLE, typefaceStyleIndex)
|
||||
putFloat(KEY_OUTLINE_SIZE, outlineSize)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun saveQuotes(quotes: Array<String>) {
|
||||
prefs.edit()
|
||||
.putString(KEY_QUOTES, quotes.joinToString(SEPARATOR.toString()))
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.widget.RemoteViews
|
||||
import android.widget.TextView
|
||||
|
||||
class TextWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
|
||||
val remoteViews = configureViews(context, appWidgetIds)
|
||||
// Perform this loop procedure for each App Widget that belongs to this provider
|
||||
appWidgetIds.forEach { appWidgetId ->
|
||||
// Tell the AppWidgetManager to perform an update on the current app widget
|
||||
appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
|
||||
}
|
||||
}
|
||||
|
||||
private fun configureViews(context: Context, appWidgetIds: IntArray): RemoteViews =
|
||||
RemoteViews(context.packageName, R.layout.widget_text).apply {
|
||||
|
||||
val storage = QuoteStorage(context)
|
||||
|
||||
val quote = if (storage.isOrderRandom()) {
|
||||
storage.getRandomQuote()
|
||||
} else {
|
||||
storage.getNextQuote()
|
||||
}
|
||||
|
||||
setTextViewText(R.id.widget_text, quote.ifBlank { context.getString(R.string.info_add_text) })
|
||||
setTextViewTextSize(R.id.widget_text, TypedValue.COMPLEX_UNIT_SP, storage.getTextSize().toFloat())
|
||||
|
||||
val colours = storage.getColours(context = context)
|
||||
|
||||
setTextColor(R.id.widget_text, colours.first)
|
||||
setInt(R.id.background, "setBackgroundColor", colours.second)
|
||||
|
||||
val gravity = when (storage.getWidgetGravity()) {
|
||||
0 -> Gravity.START or Gravity.TOP
|
||||
1 -> Gravity.CENTER_HORIZONTAL or Gravity.TOP
|
||||
2 -> Gravity.END or Gravity.TOP
|
||||
3 -> Gravity.START or Gravity.CENTER_VERTICAL
|
||||
4 -> Gravity.CENTER
|
||||
5 -> Gravity.END or Gravity.CENTER_VERTICAL
|
||||
6 -> Gravity.START or Gravity.BOTTOM
|
||||
7 -> Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
|
||||
8 -> Gravity.END or Gravity.BOTTOM
|
||||
else -> Gravity.CENTER
|
||||
}
|
||||
|
||||
setInt(R.id.root_layout, "setGravity", gravity)
|
||||
|
||||
setOnClickPendingIntent(
|
||||
R.id.root_layout,
|
||||
Intent(context, TextWidget::class.java).let { intent ->
|
||||
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
|
||||
PendingIntent.getBroadcast(
|
||||
context, 0, intent, PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.denizd.textbasic
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
|
||||
class VerticalTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : androidx.appcompat.widget.AppCompatTextView(context, attrs, defStyle) {
|
||||
|
||||
private var _width = 0
|
||||
private var _height = 0
|
||||
private val _bounds: Rect = Rect()
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
// vise versa
|
||||
_height = measuredWidth
|
||||
_width = measuredHeight
|
||||
setMeasuredDimension(_width, _height)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
canvas.save()
|
||||
canvas.translate(_width.toFloat(), _height.toFloat())
|
||||
canvas.rotate(-90f)
|
||||
val paint = paint
|
||||
paint.color = textColors.defaultColor
|
||||
val text = text()
|
||||
paint.getTextBounds(text, 0, text.length, _bounds)
|
||||
canvas.drawText(text, compoundPaddingLeft.toFloat(), (_bounds.height() - _width) / 2f, paint)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
private fun text(): String {
|
||||
return super.getText().toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.denizd.textbasic.fragment
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
open class BaseFragment(@LayoutRes layoutId: Int) : Fragment(layoutId) {
|
||||
|
||||
private lateinit var _context: Context
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
_context = context
|
||||
}
|
||||
|
||||
override fun getContext(): Context = _context
|
||||
|
||||
// protected fun RecyclerView.addFabScrollListener() {
|
||||
// addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
// override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
// when {
|
||||
// dy > 0 -> fab.hide()
|
||||
// dy < 0 -> fab.show()
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// protected fun NestedScrollView.addFabScrollListener() {
|
||||
// setOnScrollChangeListener { _, _, dy, _, doy ->
|
||||
// when {
|
||||
// dy > doy -> fab.hide()
|
||||
// dy < doy -> fab.show()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.denizd.textbasic.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import com.denizd.textbasic.R
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
||||
class GuideFragment : BaseFragment(R.layout.fragment_guide) {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.denizd.textbasic.fragment
|
||||
|
||||
import android.animation.LayoutTransition
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.denizd.textbasic.QuoteAdapter
|
||||
import com.denizd.textbasic.QuoteStorage
|
||||
import com.denizd.textbasic.util.viewBinding
|
||||
import com.denizd.textbasic.R
|
||||
import com.denizd.textbasic.databinding.FragmentQuoteBinding
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
||||
class QuoteFragment : BaseFragment(R.layout.fragment_quote) {
|
||||
|
||||
private lateinit var storage: QuoteStorage
|
||||
private lateinit var quoteAdapter: QuoteAdapter
|
||||
|
||||
private val binding: FragmentQuoteBinding by viewBinding(FragmentQuoteBinding::bind)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.quoteConstraintLayout.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
|
||||
|
||||
storage = QuoteStorage(context)
|
||||
quoteAdapter = QuoteAdapter(storage.getAllQuotes())
|
||||
|
||||
binding.quoteScroller.let { s ->
|
||||
s.layoutManager = LinearLayoutManager(context)
|
||||
s.adapter = quoteAdapter
|
||||
}
|
||||
|
||||
binding.addFab.setOnClickListener {
|
||||
quoteAdapter.addNewQuote()
|
||||
}
|
||||
}
|
||||
|
||||
private fun save() {
|
||||
storage.saveQuotes(quoteAdapter.getAllQuotes())
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
save()
|
||||
super.onPause()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
package com.denizd.textbasic.fragment
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.annotation.ColorInt
|
||||
import com.denizd.textbasic.ColorTransparentUtils
|
||||
import com.denizd.textbasic.QuoteStorage
|
||||
import com.denizd.textbasic.R
|
||||
import com.denizd.textbasic.databinding.FragmentSettingsBinding
|
||||
import com.denizd.textbasic.util.viewBinding
|
||||
import com.denizd.textbasic.widget.CanvasText
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.math.abs
|
||||
|
||||
class SettingsFragment : BaseFragment(R.layout.fragment_settings) {
|
||||
|
||||
private lateinit var storage: QuoteStorage
|
||||
private lateinit var gravityButtons: Array<ImageButton>
|
||||
|
||||
private var widgetGravity = 4
|
||||
private var backgroundIndex = 0
|
||||
private var typefaceIndex = 0
|
||||
private var typefaceStyleIndex = 0
|
||||
|
||||
private val binding: FragmentSettingsBinding by viewBinding(FragmentSettingsBinding::bind)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// binding.animationLl.animateLayoutChanges(true)
|
||||
|
||||
storage = QuoteStorage(context)
|
||||
|
||||
gravityButtons = arrayOf(
|
||||
binding.buttonTopLeft, binding.buttonTopMiddle, binding.buttonTopRight,
|
||||
binding.buttonMiddleLeft, binding.buttonMiddleMiddle, binding.buttonMiddleRight,
|
||||
binding.buttonBottomLeft, binding.buttonBottomMiddle, binding.buttonBottomRight
|
||||
)
|
||||
|
||||
widgetGravity = storage.getWidgetGravity()
|
||||
|
||||
binding.textSizePicker.apply {
|
||||
minValue = 1
|
||||
maxValue = 168
|
||||
descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
wrapSelectorWheel = false
|
||||
value = storage.getTextSize()
|
||||
|
||||
setOnValueChangedListener { _, _, _ ->
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
binding.transparencyPicker.apply {
|
||||
minValue = 0
|
||||
maxValue = 20
|
||||
val formatter = NumberPicker.Formatter { value ->
|
||||
val temp = value * 5
|
||||
"" + temp
|
||||
}
|
||||
setFormatter(formatter)
|
||||
descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
wrapSelectorWheel = false
|
||||
value = storage.getTransparency()
|
||||
|
||||
// Do not touch - fix for value on spinner not showing up until touched
|
||||
val f: Field = NumberPicker::class.java.getDeclaredField("mInputText")
|
||||
f.isAccessible = true
|
||||
val inputText: EditText = f.get(binding.transparencyPicker) as EditText
|
||||
inputText.filters = arrayOfNulls(0)
|
||||
|
||||
setOnValueChangedListener { _, _, _ ->
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
binding.outlinePicker.apply {
|
||||
minValue = 0
|
||||
maxValue = 64
|
||||
descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
wrapSelectorWheel = false
|
||||
value = storage.getOutlineSize().toInt()
|
||||
|
||||
setOnValueChangedListener { _, _, _ ->
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchInvert.apply {
|
||||
isChecked = storage.isInvertedEnabled()
|
||||
|
||||
setOnCheckedChangeListener { _, _ ->
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchRandom.apply {
|
||||
isChecked = storage.isOrderRandom()
|
||||
|
||||
setOnCheckedChangeListener { _, _ ->
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
// binding.switchHighContrast.apply {
|
||||
// isChecked = storage.isHighContrastEnabled()
|
||||
//
|
||||
// setOnCheckedChangeListener { _, _ ->
|
||||
// updatePreview()
|
||||
// }
|
||||
// }
|
||||
|
||||
// binding.typefaceMenuTextview.apply {
|
||||
// typefaceIndex = storage.getTypefaceIndex()
|
||||
// val typefaces = context.resources.getStringArray(R.array.typefaces)
|
||||
// val typefaceAdapter = ArrayAdapter(
|
||||
// context,
|
||||
// android.R.layout.simple_dropdown_item_1line,
|
||||
// typefaces
|
||||
// )
|
||||
// setAdapter(typefaceAdapter)
|
||||
// setText(typefaces[typefaceIndex], false)
|
||||
//
|
||||
// setOnItemClickListener { adapterView, view, index, l ->
|
||||
// typefaceIndex = index
|
||||
// updatePreview()
|
||||
// }
|
||||
// }
|
||||
|
||||
binding.typefaceToggleButton.apply {
|
||||
typefaceIndex = storage.getTypefaceIndex()
|
||||
|
||||
check(when (typefaceStyleIndex) {
|
||||
2 -> R.id.button3_monospace
|
||||
1 -> R.id.button2_serif
|
||||
else -> R.id.button1_sansserif
|
||||
})
|
||||
|
||||
addOnButtonCheckedListener { group, checkedId, isChecked ->
|
||||
typefaceIndex = when (checkedId) {
|
||||
R.id.button3_monospace -> 2
|
||||
R.id.button2_serif -> 1
|
||||
else -> 0
|
||||
}
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
binding.typefaceStyleToggleButton.apply {
|
||||
typefaceStyleIndex = storage.getTypefaceStyle()
|
||||
|
||||
check(when (typefaceStyleIndex) {
|
||||
3 -> R.id.button4_bold_italics
|
||||
2 -> R.id.button3_italics
|
||||
1 -> R.id.button2_bold
|
||||
else -> R.id.button1_normal
|
||||
})
|
||||
|
||||
addOnButtonCheckedListener { group, checkedId, isChecked ->
|
||||
typefaceStyleIndex = when (checkedId) {
|
||||
R.id.button4_bold_italics -> 3
|
||||
R.id.button3_italics -> 2
|
||||
R.id.button2_bold -> 1
|
||||
else -> 0
|
||||
}
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
binding.backgroundToggleButton.apply {
|
||||
backgroundIndex = storage.getBackgroundType()
|
||||
|
||||
check(when (backgroundIndex) {
|
||||
2 -> R.id.button3_shadow
|
||||
1 -> R.id.button2_outline
|
||||
else -> R.id.button1_background
|
||||
})
|
||||
|
||||
addOnButtonCheckedListener { group, checkedId, isChecked ->
|
||||
backgroundIndex = when (checkedId) {
|
||||
R.id.button3_shadow -> 2
|
||||
R.id.button2_outline -> 1
|
||||
else -> 0
|
||||
}
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
gravityButtons.forEachIndexed { index, button ->
|
||||
button.setOnClickListener {
|
||||
widgetGravity = index
|
||||
setGravityButtons()
|
||||
updatePreview()
|
||||
}
|
||||
}
|
||||
|
||||
setGravityButtons()
|
||||
updatePreview()
|
||||
}
|
||||
|
||||
private fun setGravityButtons() {
|
||||
val transparent = context.getColor(android.R.color.transparent)
|
||||
|
||||
gravityButtons.forEach { button ->
|
||||
button.setBackgroundColor(transparent)
|
||||
}
|
||||
|
||||
val typedValue = TypedValue()
|
||||
context.theme.resolveAttribute(R.attr.colorSecContainer, typedValue, true)
|
||||
@ColorInt val colorTertiary = typedValue.data
|
||||
|
||||
gravityButtons[widgetGravity].setBackgroundColor(colorTertiary)
|
||||
}
|
||||
|
||||
private fun updatePreview() {
|
||||
|
||||
save()
|
||||
|
||||
if (backgroundIndex == 0) {
|
||||
binding.outlineTextview.visibility = View.GONE
|
||||
binding.outlinePicker.visibility = View.GONE
|
||||
} else {
|
||||
binding.outlineTextview.visibility = View.VISIBLE
|
||||
binding.outlinePicker.visibility = View.VISIBLE
|
||||
if (backgroundIndex == 1) {
|
||||
binding.outlineTextview.text = getString(R.string.thickness)
|
||||
} else {
|
||||
binding.outlineTextview.text = getString(R.string.intensity)
|
||||
}
|
||||
}
|
||||
|
||||
// val isBackgroundDark = if (storage.isInvertedEnabled()) { // bg white
|
||||
// (storage.getTransparency() * 5) <= 5
|
||||
// } else { // bg black
|
||||
// (storage.getTransparency() * 5) > 5
|
||||
// }
|
||||
|
||||
// Log.d("ASDF", isBackgroundDark.toString())
|
||||
|
||||
// binding.textPreviewBackground.setBackgroundColor(
|
||||
// Color.parseColor(ColorTransparentUtils.transparentColor(context.getColor(if (isBackgroundDark) {
|
||||
// R.color.md_theme_light_background
|
||||
// } else {
|
||||
// R.color.md_theme_dark_background
|
||||
// }), storage.getTransparency() * 5)))
|
||||
|
||||
// binding.textPreviewBackground.setBackgroundColor(
|
||||
// storage.getColours(
|
||||
// !storage.isInvertedEnabled(),
|
||||
// abs(storage.getTransparency() - 100),
|
||||
// context = context
|
||||
// ).second
|
||||
// )
|
||||
|
||||
binding.textPreviewBackgroundDark.setBackgroundColor(
|
||||
Color.parseColor(ColorTransparentUtils.transparentColor(
|
||||
context.getColor(R.color.md_theme_dark_background),
|
||||
if (storage.isInvertedEnabled()) {
|
||||
abs((storage.getTransparency() * 5))
|
||||
} else {
|
||||
abs((storage.getTransparency() * 5) - 100)
|
||||
}
|
||||
))
|
||||
)
|
||||
|
||||
val bitmap = CanvasText.drawText(context, true)
|
||||
|
||||
binding.textPreview.setImageBitmap(bitmap)
|
||||
|
||||
// val colours = storage.getColours(
|
||||
// binding.switchInvert.isChecked,
|
||||
//// binding.switchHighContrast.isChecked,
|
||||
// binding.transparencyPicker.value,
|
||||
// context
|
||||
// )
|
||||
//// binding.exampleText.textSize = binding.textSizePicker.value.toFloat()
|
||||
// binding.exampleText.setTextColor(colours.first)
|
||||
// binding.background.setBackgroundColor(colours.second)
|
||||
|
||||
// Log.d("VALVALUE", "${(binding.transparencyPicker.value * 5).toString()} /// ${colours.second}")
|
||||
}
|
||||
|
||||
private fun save() {
|
||||
storage.saveSettings(
|
||||
binding.textSizePicker.value,
|
||||
binding.switchInvert.isChecked,
|
||||
// binding.switchHighContrast.isChecked,
|
||||
binding.transparencyPicker.value,
|
||||
binding.switchRandom.isChecked,
|
||||
widgetGravity,
|
||||
backgroundIndex,
|
||||
typefaceIndex,
|
||||
typefaceStyleIndex,
|
||||
binding.outlinePicker.value.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
save()
|
||||
super.onPause()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.denizd.textbasic.util
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewbinding.ViewBinding
|
||||
|
||||
// written by Zhuinden (props to them!)
|
||||
inline fun <T : ViewBinding> FragmentActivity.viewBinding(crossinline bindingInflater: (LayoutInflater) -> T) =
|
||||
lazy(LazyThreadSafetyMode.NONE) {
|
||||
bindingInflater.invoke(layoutInflater)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.denizd.textbasic.util
|
||||
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.observe
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
// written by Zhuinden (props to them!)
|
||||
class FragmentViewBindingDelegate<T : ViewBinding>(
|
||||
val fragment: Fragment,
|
||||
val viewBindingFactory: (View) -> T
|
||||
) : ReadOnlyProperty<Fragment, T> {
|
||||
private var _binding: T? = null
|
||||
|
||||
init {
|
||||
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
|
||||
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
_binding = null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
|
||||
val binding = _binding
|
||||
if (binding != null) {
|
||||
return binding
|
||||
}
|
||||
|
||||
val lifecycle = fragment.viewLifecycleOwner.lifecycle
|
||||
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
|
||||
throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.")
|
||||
}
|
||||
|
||||
return viewBindingFactory(thisRef.requireView()).also { _binding = it }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
|
||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||
@@ -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