Skip to content

Commit

Permalink
Implement graphics note's thumb in Note list
Browse files Browse the repository at this point in the history
  • Loading branch information
tuancoltech committed Oct 27, 2024
1 parent fa8f4e1 commit 11e8bf1
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 29 deletions.
21 changes: 21 additions & 0 deletions app/src/main/java/dev/arkbuilders/arkmemo/di/AppModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.arkbuilders.arkmemo.di

import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

@Provides
@Singleton
fun provideApplicationContext(@ApplicationContext context: Context): Context {
return context
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ internal object ColorCode {
val purple by lazy { android.graphics.Color.parseColor("#7A5AF8") }
val white by lazy { android.graphics.Color.parseColor("#FFFFFF") }
val brown by lazy { android.graphics.Color.parseColor("#B54708") }
val lightYellow by lazy { android.graphics.Color.parseColor("#f8f6ed") }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.arkbuilders.arkmemo.models

import android.graphics.Bitmap
import android.os.Parcelable
import dev.arkbuilders.arklib.data.index.Resource
import dev.arkbuilders.arkmemo.graphics.SVG
Expand All @@ -14,5 +15,7 @@ data class GraphicNote(
val svg: SVG? = null,
@IgnoredOnParcel
override var resource: Resource? = null,
override var pendingForDelete: Boolean = false
override var pendingForDelete: Boolean = false,
var thumb: Bitmap? = null

) : Note, Parcelable
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
package dev.arkbuilders.arkmemo.repo.graphics

import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.os.Environment
import android.util.Log
import dagger.hilt.android.qualifiers.ApplicationContext
import dev.arkbuilders.arklib.computeId
import dev.arkbuilders.arklib.data.index.Resource
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.di.IO_DISPATCHER
import dev.arkbuilders.arkmemo.graphics.ColorCode
import dev.arkbuilders.arkmemo.models.GraphicNote
import dev.arkbuilders.arkmemo.preferences.MemoPreferences
import dev.arkbuilders.arkmemo.graphics.SVG
import dev.arkbuilders.arkmemo.models.SaveNoteResult
import dev.arkbuilders.arkmemo.repo.NotesRepo
import dev.arkbuilders.arkmemo.repo.NotesRepoHelper
import dev.arkbuilders.arkmemo.utils.dpToPx
import dev.arkbuilders.arkmemo.utils.listFiles
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.nio.file.Path
import javax.inject.Inject
import javax.inject.Named
Expand All @@ -26,11 +39,19 @@ import kotlin.io.path.exists
class GraphicNotesRepo @Inject constructor(
private val memoPreferences: MemoPreferences,
@Named(IO_DISPATCHER) private val iODispatcher: CoroutineDispatcher,
private val helper: NotesRepoHelper
private val helper: NotesRepoHelper,
@ApplicationContext private val context: Context
): NotesRepo<GraphicNote> {

private lateinit var root: Path

private val displayMetrics by lazy { Resources.getSystem().displayMetrics }
private val screenWidth by lazy { displayMetrics.widthPixels }
private val screenHeight by lazy { displayMetrics.heightPixels - 150.dpToPx() }
private val thumbViewWidth by lazy { context.resources.getDimension(R.dimen.graphic_thumb_width) }

private val thumbDirectory by lazy { context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) }

override suspend fun init() {
helper.init()
root = memoPreferences.getNotesStorage()
Expand Down Expand Up @@ -102,15 +123,76 @@ class GraphicNotesRepo @Inject constructor(
)

val userNoteProperties = helper.readProperties(id, "")
val bitmap = exportBitmapFromSvg(fileName = id.toString(), svg = svg)

GraphicNote(
title = userNoteProperties.title,
description = userNoteProperties.description,
svg = svg,
resource = resource
resource = resource,
thumb = bitmap
)

}.filter { graphicNote -> graphicNote.svg != null }
}

private fun exportBitmapFromSvg(fileName: String, svg: SVG?): Bitmap? {

// Check if thumb bitmap already exists
val file = File(thumbDirectory, "$fileName.png")
try {
if (file.exists()) {
return BitmapFactory.decodeFile(file.absolutePath)
}
} catch (e: Exception) {
e.printStackTrace()
}

// If thumb doesn't exist, create a bitmap and a canvas for offscreen drawing
val bitmap = Bitmap.createBitmap(
thumbViewWidth.toInt(), thumbViewWidth.toInt(), Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)

canvas.drawColor(ColorCode.lightYellow)
svg?.getPaths()?.forEach { path ->

canvas.save()

// Scale factor to fit the SVG path into the view
val scaleX = thumbViewWidth / screenWidth
val scaleY = thumbViewWidth / screenHeight

// Find the smallest scale to maintain the aspect ratio
val scale = minOf(scaleX, scaleY)

// Center the path in the view
val dx = (thumbViewWidth - screenWidth * scale) / 2f
val dy = (thumbViewWidth - screenHeight * scale) / 2f

// Apply scaling and translation to center the path
canvas.translate(dx, dy)
canvas.scale(scale, scale)

canvas.drawPath(path.path, path.paint)
canvas.restore()
} ?: let {
return null
}

// Save the bitmap to a file
try {

// Open an output stream and write the bitmap to the file
FileOutputStream(file).use { outputStream ->
bitmap.compress(Bitmap.CompressFormat.PNG, 80, outputStream) // Save as PNG
}
return bitmap
} catch (e: IOException) {
e.printStackTrace()
return null
}
}
}

private const val GRAPHICS_REPO = "GraphicNotesRepo"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.arkbuilders.arkmemo.ui.adapters

import android.graphics.drawable.BitmapDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Expand All @@ -8,6 +9,8 @@ import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.ShapeAppearanceModel
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.databinding.AdapterTextNoteBinding
import dev.arkbuilders.arkmemo.models.GraphicNote
Expand All @@ -20,7 +23,6 @@ import dev.arkbuilders.arkmemo.ui.fragments.EditGraphicNotesFragment
import dev.arkbuilders.arkmemo.ui.fragments.EditTextNotesFragment
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerSideEffect
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerState
import dev.arkbuilders.arkmemo.ui.views.NotesCanvas
import dev.arkbuilders.arkmemo.utils.getAutoTitle
import dev.arkbuilders.arkmemo.utils.gone
import dev.arkbuilders.arkmemo.utils.highlightWord
Expand All @@ -31,7 +33,6 @@ import dev.arkbuilders.arkmemo.utils.visible
class NotesListAdapter(
private var notes: MutableList<Note>,
private val onPlayPauseClick: (path: String, pos: Int?, stopCallback: ((pos: Int) -> Unit)?) -> Unit,
private val onThumbPrepare : (note: GraphicNote, holder: NotesCanvas) -> Unit
): RecyclerView.Adapter<NotesListAdapter.NoteViewHolder>() {

private lateinit var activity: MainActivity
Expand All @@ -42,6 +43,10 @@ class NotesListAdapter(
private var isFromSearch: Boolean = false
private var searchKeyWord: String = ""

private val cornerRadius by lazy {
activity.resources.getDimension(R.dimen.corner_radius_big)
}

fun setActivity(activity: AppCompatActivity) {
this.activity = activity as MainActivity
}
Expand All @@ -64,6 +69,7 @@ class NotesListAdapter(
holder.contentPreview.text = note.text
}
holder.layoutAudioView.root.gone()
holder.ivGraphicThumb.gone()
if (note is VoiceNote) {
val isRecordingExist = note.path.toFile().length() > 0L
if (isRecordingExist) {
Expand Down Expand Up @@ -105,12 +111,24 @@ class NotesListAdapter(
}

} else if (note is GraphicNote) {
holder.canvasGraphicThumb.visible()
onThumbPrepare(note, holder.canvasGraphicThumb)
holder.ivGraphicThumb.background = BitmapDrawable(
holder.itemView.context.resources, note.thumb
)
holder.ivGraphicThumb.visible()
holder.ivGraphicThumb.shapeAppearanceModel = ShapeAppearanceModel.builder()
.setBottomLeftCornerSize(0f)
.setTopLeftCornerSize(0f)
.setTopRightCorner(CornerFamily.ROUNDED, cornerRadius)
.setBottomRightCorner(CornerFamily.ROUNDED, cornerRadius)
.build()
}

if (note.pendingForDelete) {
holder.tvDelete.visible()
if (note is GraphicNote) {
holder.ivGraphicThumb.shapeAppearanceModel = ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.ROUNDED, 0f).build()
}
} else {
holder.tvDelete.gone()
}
Expand Down Expand Up @@ -194,7 +212,7 @@ class NotesListAdapter(
val btnPlayPause = binding.layoutAudioView.ivPlayAudio
val layoutAudioView = binding.layoutAudioView
val tvPlayingPosition = binding.layoutAudioView.tvPlayingPosition
val canvasGraphicThumb = binding.canvasGraphicThumb
val ivGraphicThumb = binding.ivGraphicsThumb
val tvDelete = binding.tvDelete
var isSwiping: Boolean = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class CommonActionDialog(@StringRes private val title: Int,
private fun initViews() {

dialog?.setCanceledOnTouchOutside(false)
dialog?.setCancelable(false)

if (isAlert) {
mBinding.tvPositive.setTextAppearance(R.style.AlertButton)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
Expand All @@ -27,7 +26,6 @@ import dev.arkbuilders.arkmemo.ui.adapters.NotesListAdapter
import dev.arkbuilders.arkmemo.ui.dialogs.CommonActionDialog
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerSideEffect
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerViewModel
import dev.arkbuilders.arkmemo.ui.viewmodels.GraphicNotesViewModel
import dev.arkbuilders.arkmemo.ui.viewmodels.NotesViewModel
import dev.arkbuilders.arkmemo.ui.views.toast
import dev.arkbuilders.arkmemo.utils.getTextFromClipBoard
Expand Down Expand Up @@ -192,7 +190,7 @@ class NotesFragment: BaseFragment() {
onPlayPauseClick = { path, pos, onStop ->
playingAudioPath = path
if (playingAudioPosition >= 0) {
refreshVoiceNoteItem(playingAudioPosition)
refreshNoteItem(playingAudioPosition)
}

if (playingAudioPosition >= 0 && playingAudioPosition != pos) {
Expand All @@ -210,11 +208,6 @@ class NotesFragment: BaseFragment() {
}

arkMediaPlayerViewModel.onPlayOrPauseClick(path, pos, onStop)
},
onThumbPrepare = { graphicNote, noteCanvas ->
val tempNoteViewModel: GraphicNotesViewModel by viewModels()
noteCanvas.setViewModel(viewModel = tempNoteViewModel)

}
)

Expand Down Expand Up @@ -261,7 +254,7 @@ class NotesFragment: BaseFragment() {
(notesAdapter?.getNotes()?.getOrNull(pos) as? VoiceNote)?.waitToBeResumed = true
}

private fun refreshVoiceNoteItem(position: Int) {
private fun refreshNoteItem(position: Int) {
notesAdapter?.notifyItemChanged(position)
}

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/bg_big_radius.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

<stroke android:width="1dp" android:color="@color/border_primary"/>
<stroke android:width="@dimen/border_width_thin" android:color="@color/border_primary"/>
<corners android:radius="@dimen/corner_radius_big"/>

</shape>
31 changes: 20 additions & 11 deletions app/src/main/res/layout/adapter_text_note.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
android:textColor="@color/text_primary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/canvas_graphic_thumb"
app:layout_constraintEnd_toStartOf="@+id/iv_graphics_thumb"
android:gravity="start"
app:layout_constraintTop_toBottomOf="@+id/layout_audio_view"
android:maxLines="1"
Expand All @@ -46,7 +46,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/canvas_graphic_thumb"
app:layout_constraintEnd_toStartOf="@+id/iv_graphics_thumb"
android:maxLines="2"
android:ellipsize="end"
android:layout_marginTop="4dp"
Expand All @@ -55,17 +55,18 @@
android:id="@+id/tv_content_preview"
tools:text="How do you create compelling presentations that wow your colleagues and impress your managers?"/>

<dev.arkbuilders.arkmemo.ui.views.NotesCanvas
android:id="@+id/canvas_graphic_thumb"
android:layout_width="90dp"
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/iv_graphics_thumb"
android:layout_width="@dimen/graphic_thumb_width"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
android:paddingStart="10dp"
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:paddingBottom="80dp"
app:layout_constraintEnd_toStartOf="@+id/barrier_graphic_thumb_end"
android:layout_marginEnd="@dimen/border_width_thin"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginVertical="@dimen/border_width_thin"
app:shapeAppearanceOverlay="@style/GraphicThumbBackground"
android:visibility="gone"
tools:visibility="gone"/>
tools:visibility="visible"/>

<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
Expand Down Expand Up @@ -96,4 +97,12 @@
android:visibility="gone"
android:gravity="center"/>

<androidx.constraintlayout.widget.Barrier
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/barrier_graphic_thumb_end"
app:barrierDirection="start"
app:constraint_referenced_ids="tv_delete"
app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
1 change: 1 addition & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<color name="brush_color_green">#17B26A</color>
<color name="brush_color_blue">#0BA5EC</color>
<color name="brush_color_purple">#7A5AF8</color>
<color name="graphic_thumb_background">#FBF8F0</color>

<!-- Custom SwitchButton -->
<color name="sb_unchecked_circle_color">#FFAAAAAA</color>
Expand Down
Loading

0 comments on commit 11e8bf1

Please sign in to comment.