Skip to content

Commit

Permalink
* [feat] Implement new Settings UI with contribute and support options
Browse files Browse the repository at this point in the history
* Upgrade Kotlin version to 1.9.0, and Hilt version respectively
* Add Coil for image loading
  • Loading branch information
tuancoltech committed May 27, 2024
1 parent e0ebd30 commit 30511a3
Show file tree
Hide file tree
Showing 36 changed files with 911 additions and 28 deletions.
8 changes: 5 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {

android {
namespace 'dev.arkbuilders.arkmemo'
compileSdk 33
compileSdk 34

defaultConfig {
applicationId "dev.arkbuilders.arkmemo"
Expand Down Expand Up @@ -90,8 +90,8 @@ dependencies {
implementation 'dev.arkbuilders:arklib:0.3.3'

implementation 'androidx.preference:preference:1.2.0'
implementation "com.google.dagger:hilt-android:2.42"
kapt "com.google.dagger:hilt-compiler:2.42"
implementation "com.google.dagger:hilt-android:2.48"
kapt "com.google.dagger:hilt-compiler:2.48"
kapt 'androidx.hilt:hilt-compiler:1.0.0'

implementation 'com.github.kirich1409:viewbindingpropertydelegate-noreflection:1.5.9'
Expand All @@ -105,4 +105,6 @@ dependencies {
implementation 'com.simplemobiletools:commons:5.29.20'

implementation 'com.airbnb.android:lottie:6.4.0'
implementation 'com.github.androidmads:QRGenerator:1.0.1'
implementation("io.coil-kt:coil:2.6.0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ class NotesListAdapter(
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
val note = notes[position]
holder.title.text = note.getAutoTitle(activity)
// holder.date.text = note.resource?.modified?.toString() ?:
// activity.getString(R.string.ark_memo_just_now)
if (note is TextNote) {
holder.contentPreview.text = note.text
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package dev.arkbuilders.arkmemo.ui.dialogs

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import coil.load
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.databinding.DialogDonateQrBinding
import dev.arkbuilders.arkmemo.ui.viewmodels.QRViewModel
import dev.arkbuilders.arkmemo.ui.views.toast
import dev.arkbuilders.arkmemo.utils.copyToClipboard


class DonateDialog(private val walletAddress: String,
private val onPositiveClick: (() -> Unit)? = null,
private val onCloseClicked: (() -> Unit)? = null,
): DialogFragment() {

companion object {
val TAG: String = DonateDialog::class.java.name
}
private lateinit var binding: DialogDonateQrBinding
private val qrViewModel: QRViewModel by activityViewModels()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = DialogDonateQrBinding.inflate(inflater)
initViews()
return binding.root
}

private fun initViews() {

dialog?.setCanceledOnTouchOutside(false)

binding.ivClose.setOnClickListener {
onCloseClicked?.invoke()
dismiss()
}

binding.layoutDownloadQr.setOnClickListener {
onPositiveClick?.invoke()
dismiss()
}

binding.tvAddress.text = walletAddress
binding.layoutCopy.setOnClickListener {
context?.copyToClipboard(getString(R.string.setting_donate_wallet_clipboard_label),
walletAddress)
}

initQRImage()
}

private fun initQRImage() {
qrViewModel.generateQRCode(walletAddress) { bitmap ->
binding.ivQr.load(bitmap)
binding.layoutDownloadQr.setOnClickListener {
qrViewModel.saveQRCodeImage(walletAddress, bitmap) { path ->
context?.let { ctx ->
toast(ctx, getString(R.string.toast_save_qr_success, path))
}
}
}
}
}

override fun onResume() {
super.onResume()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
}

override fun getTheme(): Int {
return R.style.MemoDialog
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ class NotesFragment: Fragment() {
},
onThumbPrepare = { graphicNote, noteCanvas ->
val tempNoteViewModel: GraphicNotesViewModel by viewModels()
noteCanvas.setViewModel(viewModel = /*graphicNotesViewModel*/tempNoteViewModel)
// tempNoteViewModel.onNoteOpened(graphicNote)
noteCanvas.setViewModel(viewModel = tempNoteViewModel)

}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import by.kirich1409.viewbindingdelegate.viewBinding
import dev.arkbuilders.arkmemo.BuildConfig
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.databinding.FragmentSettingsBinding
import dev.arkbuilders.arkmemo.ui.dialogs.CommonActionDialog
import dev.arkbuilders.arkmemo.ui.dialogs.DonateDialog
import dev.arkbuilders.arkmemo.utils.gone
import dev.arkbuilders.arkmemo.utils.openLink
import dev.arkbuilders.arkmemo.utils.visible

open class SettingsFragmentV2: Fragment(R.layout.fragment_settings) {
Expand All @@ -24,5 +28,46 @@ open class SettingsFragmentV2: Fragment(R.layout.fragment_settings) {
binding.toolbarCustom.tvRightActionText.gone()
binding.toolbarCustom.ivRightActionIcon.gone()

binding.tvAppVersion.text = getString(R.string.setting_app_version, BuildConfig.VERSION_NAME)

initSettingActions()

}

private fun initSettingActions() {
binding.tvWebsite.setOnClickListener {
context?.openLink("https://www.ark-builders.dev/")
}

binding.tvTelegram.setOnClickListener {
context?.openLink("https://t.me/ark_builders")
}

binding.tvDonatePatreon.setOnClickListener {
context?.openLink("https://www.patreon.com/ARKBuilders")
}

binding.tvDonateCoffee.setOnClickListener {
context?.openLink("https://buymeacoffee.com/arkbuilders")
}

binding.tvDonateBtc.setOnClickListener {
DonateDialog(
walletAddress = "bc1qx8n9r4uwpgrhgnamt2uew53lmrxd8tuevp7lv5",
onPositiveClick = {
}).show(childFragmentManager, CommonActionDialog.TAG)
}

binding.tvDonateEth.setOnClickListener {
DonateDialog(
walletAddress = "0x9765C5aC38175BFbd2dC7a840b63e50762B80a1b",
onPositiveClick = {
}).show(childFragmentManager, CommonActionDialog.TAG)
}

binding.tvDiscoverIssues.setOnClickListener {
context?.openLink("https://www.ark-builders.dev/contribute/?tab=goodFirstIssue")
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package dev.arkbuilders.arkmemo.ui.viewmodels

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import androidmads.library.qrgenearator.QRGContents
import androidmads.library.qrgenearator.QRGEncoder
import androidmads.library.qrgenearator.QRGSaver
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.arkbuilders.arkmemo.di.IO_DISPATCHER
import dev.arkbuilders.arkmemo.utils.dp2Px
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject
import javax.inject.Named


@SuppressLint("StaticFieldLeak")
@HiltViewModel
class QRViewModel @Inject constructor(
@Named(IO_DISPATCHER) private val iODispatcher: CoroutineDispatcher,
@ApplicationContext private val context: Context,
) : ViewModel() {

fun generateQRCode(text: String, onSuccess: (bitmap: Bitmap) -> Unit) {
viewModelScope.launch(iODispatcher) {
// Initializing the QR Encoder with your value to be encoded, type you required and Dimension
val qrgEncoder = QRGEncoder(text, null, QRGContents.Type.TEXT, 300.dp2Px())
qrgEncoder.colorBlack = Color.BLACK
qrgEncoder.colorWhite = Color.WHITE
withContext(Dispatchers.Main) {
onSuccess.invoke(qrgEncoder.getBitmap(0))
}
}
}

fun saveQRCodeImage(text: String, bitmap: Bitmap, onSuccess: (path: String) -> Unit) {
viewModelScope.launch {
// Save with location, value, bitmap returned and type of Image(JPG/PNG).
val qrgSaver = QRGSaver()

val savePath = (context.getExternalFilesDir(null)?.path + "/images/").apply {
File(this).mkdirs()
}

val isSuccess = qrgSaver.save(
savePath,
text,
bitmap,
QRGContents.ImageType.IMAGE_JPEG
)

if (isSuccess) {
onSuccess.invoke(savePath)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class GraphicControlTextView @JvmOverloads constructor(
)
} else {
this.background = ContextCompat.getDrawable(
context, R.drawable.bg_search_bar
context, R.drawable.bg_border_r8
)
val selectedColor = ContextCompat.getColor(context, R.color.text_tertiary)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package dev.arkbuilders.arkmemo.ui.views

import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.databinding.LayoutSupportTextBinding
import dev.arkbuilders.arkmemo.utils.gone

class SupportTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ConstraintLayout(context, attrs) {
init {
val binding = LayoutSupportTextBinding.inflate(LayoutInflater.from(context), this, true)
val typedArray: TypedArray =
context.obtainStyledAttributes(attrs, R.styleable.SupportTextView)
val textResId = typedArray.getText(R.styleable.SupportTextView_support_text)
val iconResId = typedArray.getResourceId(R.styleable.SupportTextView_support_icon, 0)
val logoResId = typedArray.getResourceId(R.styleable.SupportTextView_support_logo, 0)
val enabled = typedArray.getBoolean(R.styleable.SupportTextView_support_enabled, true)
textResId?.let {
binding.tvText.text = textResId
binding.ivLogoText.gone()
} ?: let {
binding.tvText.gone()
binding.ivIcon.gone()
binding.ivLogoText.setImageResource(logoResId)
}

if (iconResId != 0) {
binding.ivIcon.setImageResource(iconResId)
} else {
binding.ivIcon.gone()
}

if (!enabled) {
binding.tvText.setTextColor(ContextCompat.getColor(context, R.color.gray_400))
binding.tvText.isEnabled = false
}

typedArray.recycle()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ class WaveView(context: Context, attrs: AttributeSet): View(context, attrs) {

private val bars = ArrayDeque<Rect>()

override fun onDraw(canvas: Canvas?) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (bars.isNotEmpty()) {
bars.forEach {
canvas?.drawRect(it, paint)
canvas.drawRect(it, paint)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.arkbuilders.arkmemo.ui.views

import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.databinding.LayoutWebLinkTextBinding
import dev.arkbuilders.arkmemo.utils.gone

class WebLinkTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ConstraintLayout(context, attrs) {

init {
val binding = LayoutWebLinkTextBinding.inflate(LayoutInflater.from(context), this, true)
val typedArray: TypedArray =
context.obtainStyledAttributes(attrs, R.styleable.WebLinkTextView)
val textResId = typedArray.getText(R.styleable.WebLinkTextView_web_link_text)
val iconResId = typedArray.getResourceId(R.styleable.WebLinkTextView_web_link_icon, 0)
textResId?.let {
binding.tvText.text = textResId
}

if (iconResId != 0) {
binding.ivIcon.setImageResource(iconResId)
} else {
binding.ivIcon.gone()
}

typedArray.recycle()
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/dev/arkbuilders/arkmemo/utils/ContextExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.arkbuilders.arkmemo.utils

import android.content.Context
import android.content.Intent
import android.net.Uri

fun Context.openLink(url: String) {
startActivity(
Intent(Intent.ACTION_VIEW).setData(Uri.parse(url)))
}
Loading

0 comments on commit 30511a3

Please sign in to comment.