Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow downloading lyrics locally #30

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


<application
android:name=".FastLyricsApp"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
package io.github.teccheck.fastlyrics.ui.fastlyrics

import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.squareup.picasso.Picasso
import dev.forkhandles.result4k.Failure
import dev.forkhandles.result4k.Success
import android.widget.Toast

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.File
import java.io.FileOutputStream
import dev.forkhandles.result4k.Result
import io.github.teccheck.fastlyrics.R
import io.github.teccheck.fastlyrics.Settings
Expand All @@ -28,12 +41,20 @@ import io.github.teccheck.fastlyrics.utils.Utils
import io.github.teccheck.fastlyrics.utils.Utils.copyToClipboard
import io.github.teccheck.fastlyrics.utils.Utils.openLink
import io.github.teccheck.fastlyrics.utils.Utils.share
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.time.delay
import kotlinx.coroutines.delay
import java.time.Duration
import kotlin.time.Duration.Companion.seconds


class FastLyricsFragment : Fragment() {

private lateinit var lyricsViewModel: FastLyricsViewModel
private var _binding: FragmentFastLyricsBinding? = null
lateinit var song: SongWithLyrics

// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
Expand Down Expand Up @@ -88,7 +109,7 @@ class FastLyricsFragment : Fragment() {
binding.header.container.visibility = View.GONE
binding.errorView.container.visibility = View.GONE
binding.lyricsView.container.visibility = View.GONE

when (result) {
is Failure -> displayError(result.reason)
is Success -> displaySongMeta(result.value)
Expand Down Expand Up @@ -161,7 +182,7 @@ class FastLyricsFragment : Fragment() {
return
}

val song = (result as Success).value
song = (result as Success).value

binding.header.container.visibility = View.VISIBLE
binding.lyricsView.container.visibility = View.VISIBLE
Expand Down Expand Up @@ -197,7 +218,47 @@ class FastLyricsFragment : Fragment() {
song.title, song.artist, song.lyrics
)
}
binding.lyricsView.download.setOnClickListener {
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {

Toast.makeText(requireContext(), "Tap download again after allowing", Toast.LENGTH_SHORT).show()
CoroutineScope(Dispatchers.Main).launch {
delay(1000) // Delay for 1 second
// After delay, request permission
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
PERMISSION_REQUEST_CODE
)
}
} else {
// Permission is already granted, proceed with saving the lyrics to a file
saveLyricsToFile(requireContext(), song.title, song.lyrics)
}
}
}
private fun saveLyricsToFile(context: Context, title: String, lyrics: String) {
val externalStorageState = Environment.getExternalStorageState()
if (externalStorageState == Environment.MEDIA_MOUNTED) {
val file = File(
Environment.getExternalStorageDirectory(),
"$title.txt"
)
FileOutputStream(file).use { fos ->
fos.write(lyrics.toByteArray())
}
Toast.makeText(context, "Lyrics saved to $title.txt", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "External storage not available", Toast.LENGTH_SHORT).show()
}
}




private fun displayLyrics(song: SongWithLyrics) {
binding.lyricsView.syncedRecycler.visibility = View.GONE
Expand Down Expand Up @@ -235,5 +296,6 @@ class FastLyricsFragment : Fragment() {
companion object {
private const val TAG = "FastLyricsFragment"
private const val SCROLL_DURATION = 1000
private const val PERMISSION_REQUEST_CODE = 1001
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import io.github.teccheck.fastlyrics.model.SongWithLyrics
import io.github.teccheck.fastlyrics.service.DummyNotificationListenerService
import java.util.Timer
import kotlin.concurrent.scheduleAtFixedRate
import kotlin.math.log

class FastLyricsViewModel : ViewModel() {

private val _songMeta = MutableLiveData<Result<SongMeta, LyricsApiException>>()
private val _songWithLyrics = MutableLiveData<Result<SongWithLyrics, LyricsApiException>>()
private val _songWithLyricsSynced = MutableLiveData<Result<SongWithLyrics, LyricsApiException>>()
private val _songPosition = MutableLiveData<Long>()
var storeLyric=""

private var songPositionTimer: Timer? = null

Expand Down Expand Up @@ -55,6 +57,9 @@ class FastLyricsViewModel : ViewModel() {

if (songMetaResult is Success) {
loadLyrics(songMetaResult.value)
storeLyric=songMetaResult.value.toString()

Log.d(TAG, "Stored lyric: $storeLyric")
}

return songMetaResult is Success
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package io.github.teccheck.fastlyrics.ui.viewlyrics

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.squareup.picasso.Picasso
Expand All @@ -17,10 +24,17 @@ import io.github.teccheck.fastlyrics.model.LyricsType
import io.github.teccheck.fastlyrics.model.SearchResult
import io.github.teccheck.fastlyrics.model.SongWithLyrics
import io.github.teccheck.fastlyrics.model.SyncedLyrics
import io.github.teccheck.fastlyrics.ui.fastlyrics.FastLyricsFragment
import io.github.teccheck.fastlyrics.utils.Utils
import io.github.teccheck.fastlyrics.utils.Utils.copyToClipboard
import io.github.teccheck.fastlyrics.utils.Utils.openLink
import io.github.teccheck.fastlyrics.utils.Utils.share
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileOutputStream

class ViewLyricsFragment : Fragment() {

Expand Down Expand Up @@ -96,8 +110,46 @@ class ViewLyricsFragment : Fragment() {
song.lyrics
)
}
binding.lyricsView.download.setOnClickListener {
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {

Toast.makeText(requireContext(), "Tap download again after allowing", Toast.LENGTH_SHORT).show()
CoroutineScope(Dispatchers.Main).launch {
delay(1000) // Delay for 1 second
// After delay, request permission
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
ViewLyricsFragment.PERMISSION_REQUEST_CODE
)
}
} else {
// Permission is already granted, proceed with saving the lyrics to a file
saveLyricsToFile(requireContext(), song.title, song.lyrics)
}
}
}
private fun saveLyricsToFile(context: Context, title: String, lyrics: String) {
val externalStorageState = Environment.getExternalStorageState()
if (externalStorageState == Environment.MEDIA_MOUNTED) {
val file = File(
Environment.getExternalStorageDirectory(),
"$title.txt"
)
FileOutputStream(file).use { fos ->
fos.write(lyrics.toByteArray())
}
Toast.makeText(context, "Lyrics saved to $title.txt", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "External storage not available", Toast.LENGTH_SHORT).show()
}
}


private fun displayLyrics(song: SongWithLyrics) {
binding.lyricsView.textLyrics.text = if (song.type == LyricsType.LRC) {
val syncedLyrics = SyncedLyrics.parseLrc(song.lyrics)
Expand All @@ -120,5 +172,6 @@ class ViewLyricsFragment : Fragment() {
private const val TAG = "ViewLyricsFragment"
const val ARG_SONG_ID = "song_id"
const val ARG_SEARCH_RESULT = "search_result"
const val PERMISSION_REQUEST_CODE = 1001
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package io.github.teccheck.fastlyrics.utils

import android.content.Context
import android.os.Environment
import java.io.File
import java.io.FileWriter
import java.io.IOException

import android.content.ClipData
import android.content.ClipboardManager
import android.content.Intent
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/baseline_file_download_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>
12 changes: 12 additions & 0 deletions app/src/main/res/layout/layout_lyrics.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,21 @@
android:text="@string/share"
app:cornerRadius="32dp"
app:icon="?attr/actionModeShareDrawable" />

<com.google.android.material.button.MaterialButton
android:id="@+id/download"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Download"
app:cornerRadius="32dp"
app:icon="@drawable/baseline_file_download_24" />
</LinearLayout>

</HorizontalScrollView>


<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down