Skip to content

Commit

Permalink
Release 0.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
z-huang committed Sep 24, 2022
2 parents 4e86094 + 24aafd5 commit 82342b4
Show file tree
Hide file tree
Showing 158 changed files with 2,684 additions and 2,560 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ No ads, free, and simple.
With this app, you're like getting a free music streaming service. You can listen to music from YouTube Music and build your own library. What's more, songs can be downloaded for offline playback. You can also create playlists to organize your songs. The aim of _Music_ is to enable everyone to listen to music at no cost by an easy-to-use, practical and ad-free application.

⚠️ Warning: If you're in region that YouTube Music is not supported, you won't be able to use this app ***unless*** you have proxy or VPN to connect to a YTM supported region.

## Features

### YouTube
Expand All @@ -42,6 +44,14 @@ With this app, you're like getting a free music streaming service. You can liste
- Repeat/shuffle mode
- Edit now-playing queue

### Other

- Custom themes
- Dark theme
- Localization
- Proxy
- Backup & restore

## Screenshots

<p float="left">
Expand All @@ -68,9 +78,8 @@ You can install _Music_ using the following methods:

How to get updates?

1. If you install from [GitHub Releases](https://github.com/z-huang/music/releases), the app already has a built-in updater.
2. If you install from method 2, you can check for updates using the F-Droid application.
3. Or else, visit [GitHub](https://github.com/z-huang/music) and checkout the releases, issues, PRs, or anything new.
1. F-Droid application.
2. [GitHub](https://github.com/z-huang/music)

## Contribution

Expand Down
17 changes: 10 additions & 7 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ android {
buildToolsVersion = "30.0.3"
defaultConfig {
applicationId = "com.zionhuang.music"
minSdk = 26
minSdk = 24
targetSdk = 32
versionCode = 11
versionName = "0.4.0"
versionCode = 12
versionName = "0.4.1"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
Expand Down Expand Up @@ -48,6 +48,7 @@ android {
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility(JavaVersion.VERSION_1_8)
targetCompatibility(JavaVersion.VERSION_1_8)
}
Expand Down Expand Up @@ -109,7 +110,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0")
// AndroidX
implementation("androidx.core:core-ktx:1.8.0")
implementation("androidx.appcompat:appcompat:1.5.1")
Expand Down Expand Up @@ -153,19 +154,21 @@ dependencies {
// OkHttp
implementation("com.squareup.okhttp3:okhttp:4.10.0")
// Coil
implementation("io.coil-kt:coil:2.2.0")
implementation("io.coil-kt:coil:2.2.1")
// Fast Scroll
implementation("me.zhanghai.android.fastscroll:library:1.1.7")
implementation("me.zhanghai.android.fastscroll:library:1.1.8")
// Markdown
implementation("org.commonmark:commonmark:0.18.2")
// Desugaring
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
// Test
testImplementation("junit:junit:4.13.2")
androidTestImplementation("android.arch.core:core-testing:1.1.1")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test:runner:1.4.0")
androidTestImplementation("androidx.test:rules:1.4.0")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
testImplementation("org.mockito:mockito-core:4.3.1")
testImplementation("org.mockito:mockito-core:4.8.0")
testImplementation("org.mockito:mockito-inline:4.3.1")
testImplementation("org.mockito:mockito-android:4.3.1")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
Expand Down
12 changes: 7 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
tools:targetApi="o">
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
Expand All @@ -34,6 +35,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MUSIC_PLAYER" />

<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_MUSIC" />
Expand Down Expand Up @@ -100,16 +102,16 @@

<data android:mimeType="text/plain" />
</intent-filter>

<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>

<activity
android:name=".ui.activities.SettingsActivity"
android:parentActivityName=".ui.activities.MainActivity" />

<activity
android:name=".ui.activities.ErrorActivity"
android:parentActivityName=".ui.activities.MainActivity" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.FileProvider"
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/java/com/zionhuang/music/App.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.zionhuang.music

import android.app.Application
import android.widget.Toast
import android.widget.Toast.LENGTH_SHORT
import com.zionhuang.innertube.YouTube
import com.zionhuang.innertube.models.YouTubeLocale
import com.zionhuang.music.extensions.getEnum
import com.zionhuang.music.extensions.sharedPreferences
import com.zionhuang.music.extensions.toInetSocketAddress
import com.zionhuang.music.playback.MediaSessionConnection
import java.net.Proxy
import java.util.*

class App : Application() {
Expand All @@ -17,10 +22,17 @@ class App : Application() {
gl = sharedPreferences.getString(getString(R.string.pref_content_country), systemDefault).takeIf { it != systemDefault } ?: Locale.getDefault().country,
hl = sharedPreferences.getString(getString(R.string.pref_content_language), systemDefault).takeIf { it != systemDefault } ?: Locale.getDefault().toLanguageTag()
)

if (sharedPreferences.getBoolean(getString(R.string.pref_proxy_enabled), false)) {
try {
YouTube.setProxyUrl(sharedPreferences.getString(getString(R.string.pref_proxy_url), null) ?: "")
val socketAddress = sharedPreferences.getString(getString(R.string.pref_proxy_url), "")!!.toInetSocketAddress()
YouTube.setProxy(Proxy(
sharedPreferences.getEnum(getString(R.string.pref_proxy_type), Proxy.Type.HTTP),
socketAddress
))
} catch (e: Exception) {
// TODO
Toast.makeText(this, "Failed to parse proxy url.", LENGTH_SHORT).show()
e.printStackTrace()
}
}
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/com/zionhuang/music/constants/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ object Constants {
const val PLAYLIST_SONG_HEADER_ID = "playlist_song_header"
const val TEXT_HEADER_ID = "text_header"

const val APP_URL = "https://github.com/z-huang/music"
const val GITHUB_URL = "https://github.com/z-huang/music"
const val GITHUB_ISSUE_URL = "https://github.com/z-huang/music/issues"

const val ERROR_INFO = "error_info"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.zionhuang.music.constants

object MediaConstants {
const val EXTRA_MEDIA_METADATA = "media_metadata"
const val EXTRA_MEDIA_METADATA_ITEMS = "media_metadata_items"
const val EXTRA_SONG = "song"
const val EXTRA_ARTIST = "artist"
Expand Down
23 changes: 20 additions & 3 deletions app/src/main/java/com/zionhuang/music/db/MusicDatabase.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.zionhuang.music.db

import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase.CONFLICT_ABORT
import androidx.core.content.contentValuesOf
import androidx.room.Database
Expand Down Expand Up @@ -48,7 +49,7 @@ abstract class MusicDatabase : RoomDatabase() {
abstract val searchHistoryDao: SearchHistoryDao

companion object {
private const val DBNAME = "song.db"
const val DB_NAME = "song.db"

@Volatile
var INSTANCE: MusicDatabase? = null
Expand All @@ -57,7 +58,7 @@ abstract class MusicDatabase : RoomDatabase() {
if (INSTANCE == null) {
synchronized(MusicDatabase::class.java) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context, MusicDatabase::class.java, DBNAME)
INSTANCE = Room.databaseBuilder(context, MusicDatabase::class.java, DB_NAME)
.addMigrations(MIGRATION_1_2)
.build()
}
Expand Down Expand Up @@ -206,4 +207,20 @@ val MIGRATION_1_2 = object : Migration(1, 2) {
))
}
}
}
}

fun RoomDatabase.checkpoint() {
openHelper.writableDatabase.run {
query("PRAGMA journal_mode").use { cursor ->
if (cursor.moveToFirst()) {
when (cursor.getString(0).lowercase()) {
"wal" -> {
query("PRAGMA wal_checkpoint").use(Cursor::moveToFirst)
query("PRAGMA wal_checkpoint(TRUNCATE)").use(Cursor::moveToFirst)
query("PRAGMA wal_checkpoint").use(Cursor::moveToFirst)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ interface SearchHistoryDao {

@Query("DELETE FROM search_history WHERE `query` = :query")
suspend fun delete(query: String)

@Query("DELETE FROM search_history")
suspend fun clearHistory()
}
6 changes: 6 additions & 0 deletions app/src/main/java/com/zionhuang/music/db/daos/SongDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.zionhuang.music.db.daos
import androidx.lifecycle.LiveData
import androidx.room.*
import androidx.sqlite.db.SupportSQLiteQuery
import com.zionhuang.music.constants.MediaConstants.STATE_DOWNLOADED
import com.zionhuang.music.db.entities.*
import com.zionhuang.music.extensions.toSQLiteQuery
import com.zionhuang.music.models.sortInfo.ISortInfo
Expand Down Expand Up @@ -47,6 +48,11 @@ interface SongDao {
@Query(QUERY_PLAYLIST_SONGS)
fun getPlaylistSongsAsFlow(playlistId: String): Flow<List<Song>>

@Transaction
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT * FROM song WHERE download_state = $STATE_DOWNLOADED")
suspend fun getDownloadedSongsAsList(): List<Song>

@Transaction
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
@Query("SELECT * FROM song WHERE id = :songId")
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/com/zionhuang/music/extensions/ContextExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import androidx.core.content.res.use
import androidx.lifecycle.LifecycleOwner
import androidx.preference.PreferenceManager
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.zionhuang.music.models.toErrorInfo
import com.zionhuang.music.ui.activities.ErrorActivity
import com.zionhuang.music.utils.preference.Preference
import com.zionhuang.music.utils.preference.PreferenceLiveData
import kotlinx.coroutines.CoroutineExceptionHandler

fun Context.getDensity(): Float = resources.displayMetrics.density

Expand Down Expand Up @@ -42,3 +45,14 @@ val Context.sharedPreferences: SharedPreferences
fun <T : Any> Context.preference(@StringRes keyId: Int, defaultValue: T) = Preference(this, keyId, defaultValue)

fun <T : Any> Context.preferenceLiveData(@StringRes keyId: Int, defaultValue: T) = PreferenceLiveData(this, keyId, defaultValue)

fun Context.tryOrReport(block: () -> Unit) = try {
block()
} catch (e: Exception) {
ErrorActivity.openActivity(this, e.toErrorInfo())
}

val Context.exceptionHandler
get() = CoroutineExceptionHandler { _, throwable ->
ErrorActivity.openActivity(this, throwable.toErrorInfo())
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/zionhuang/music/extensions/FileExt.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package com.zionhuang.music.extensions

import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

operator fun File.div(child: String): File = File(this, child)

fun InputStream.zipInputStream():ZipInputStream = ZipInputStream(this)
fun OutputStream.zipOutputStream(): ZipOutputStream = ZipOutputStream(this)
7 changes: 7 additions & 0 deletions app/src/main/java/com/zionhuang/music/extensions/StringExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import androidx.sqlite.db.SimpleSQLiteQuery
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import com.google.gson.JsonSyntaxException
import java.net.InetSocketAddress
import java.net.InetSocketAddress.createUnresolved

@Throws(JsonSyntaxException::class)
fun String.parseJsonString(): JsonElement = JsonParser.parseString(this)
Expand All @@ -12,3 +14,8 @@ fun String.parseJsonString(): JsonElement = JsonParser.parseString(this)
* Database Extensions
*/
fun String.toSQLiteQuery(): SimpleSQLiteQuery = SimpleSQLiteQuery(this)

fun String.toInetSocketAddress(): InetSocketAddress {
val (host, port) = split(":")
return createUnresolved(host, port.toInt())
}
13 changes: 13 additions & 0 deletions app/src/main/java/com/zionhuang/music/models/ErrorInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.zionhuang.music.models

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class ErrorInfo(
val stackTrace: String,
) : Parcelable

fun Throwable.toErrorInfo() = ErrorInfo(
stackTraceToString()
)
Loading

0 comments on commit 82342b4

Please sign in to comment.