Skip to content

Commit

Permalink
Release 0.4.4
Browse files Browse the repository at this point in the history
  • Loading branch information
z-huang committed Dec 27, 2022
2 parents 2dcce14 + e0137e4 commit af7bd0f
Show file tree
Hide file tree
Showing 52 changed files with 752 additions and 121 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Make your own music library with any song from YouTube Music.
No ads, free, and simple.

[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/packages/com.zionhuang.music)
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" height="80">](https://apt.izzysoft.de/fdroid/index/apk/com.zionhuang.music)

[![Latest release](https://img.shields.io/github/v/release/z-huang/InnerTune?include_prereleases)](https:/z-huang/music/releases)
Expand Down Expand Up @@ -98,7 +99,13 @@ Use other music scrobbler apps. I recommend [Pano Scrobbler](https://play.google

### Q: How to export downloaded song files?

*InnerTune* support SAF. You can find the provider in Android native file manager. You can also use [Material Files](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) with [instruction](https:/z-huang/InnerTune/issues/117#issuecomment-1295090708) (recommended).
*InnerTune* supports SAF. You can find the provider in Android native file manager. You can also use [Material Files](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) with [instruction](https:/z-huang/InnerTune/issues/117#issuecomment-1295090708) (recommended).

### Q: Why InnerTune isn't showing in Android Auto?

1. Go to Android Auto's settings and tap multiple times on the version in the bottom to enable developer settings
2. In the three dots menu at the top-right of the screen, click "Developer settings"
3. Enable "Unknown sources"

## Contribution

Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ android {
applicationId = "com.zionhuang.music"
minSdk = 24
targetSdk = 32
versionCode = 14
versionName = "0.4.3"
versionCode = 15
versionName = "0.4.4"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
Expand Down
23 changes: 16 additions & 7 deletions app/src/main/java/com/zionhuang/music/App.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.zionhuang.music

import android.app.Application
import android.content.SharedPreferences
import android.os.Build
import android.util.Log
import android.widget.Toast
Expand All @@ -12,6 +13,8 @@ import coil.disk.DiskCache
import com.zionhuang.innertube.YouTube
import com.zionhuang.innertube.models.YouTubeLocale
import com.zionhuang.kugou.KuGou
import com.zionhuang.music.constants.Constants.INNERTUBE_COOKIE
import com.zionhuang.music.constants.Constants.VISITOR_DATA
import com.zionhuang.music.extensions.getEnum
import com.zionhuang.music.extensions.sharedPreferences
import com.zionhuang.music.extensions.toInetSocketAddress
Expand All @@ -22,7 +25,7 @@ import kotlinx.coroutines.launch
import java.net.Proxy
import java.util.*

class App : Application(), ImageLoaderFactory {
class App : Application(), ImageLoaderFactory, SharedPreferences.OnSharedPreferenceChangeListener {
@OptIn(DelicateCoroutinesApi::class)
override fun onCreate() {
super.onCreate()
Expand Down Expand Up @@ -61,14 +64,20 @@ class App : Application(), ImageLoaderFactory {
}

GlobalScope.launch {
val visitorData = sharedPreferences.getString(getString(R.string.pref_visitor_data), null) ?: YouTube.generateVisitorData().getOrNull()?.also {
YouTube.visitorData = sharedPreferences.getString(VISITOR_DATA, null) ?: YouTube.generateVisitorData().getOrNull()?.also {
sharedPreferences.edit {
putString(getString(R.string.pref_visitor_data), it)
putString(VISITOR_DATA, it)
}
}
visitorData?.let {
YouTube.visitorData = it
}
} ?: YouTube.DEFAULT_VISITOR_DATA
}
YouTube.cookie = sharedPreferences.getString(INNERTUBE_COOKIE, null)
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
VISITOR_DATA -> YouTube.visitorData = sharedPreferences.getString(VISITOR_DATA, null) ?: YouTube.DEFAULT_VISITOR_DATA
INNERTUBE_COOKIE -> YouTube.cookie = sharedPreferences.getString(INNERTUBE_COOKIE, null)
}
}

Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/zionhuang/music/constants/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ object Constants {
const val GITHUB_ISSUE_URL = "https:/z-huang/InnerTune/issues"

const val ERROR_INFO = "error_info"

const val VISITOR_DATA = "visitor_data"
const val INNERTUBE_COOKIE = "innertube_cookie"
const val ACCOUNT_NAME = "account_name"
const val ACCOUNT_EMAIL = "account_email"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ object MediaSessionConstants {
const val ACTION_TOGGLE_LIKE = "action_toggle_like"
const val ACTION_LIKE = "action_like"
const val ACTION_UNLIKE = "action_unlike"
const val ACTION_TOGGLE_SHUFFLE = "action_shuffle"
const val COMMAND_SEEK_TO_QUEUE_ITEM = "seek_to_queue_item"
const val COMMAND_PLAY_NEXT = "action_play_next"
const val COMMAND_ADD_TO_QUEUE = "action_add_to_queue"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ 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 <T : Any> Context.preferenceLiveData(key: String, defaultValue: T) = PreferenceLiveData(this, key, defaultValue)

fun Context.tryOrReport(block: () -> Unit) = try {
block()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import androidx.fragment.app.Fragment

fun Fragment.requireAppCompatActivity(): AppCompatActivity = requireActivity() as AppCompatActivity

val Fragment.sharedPreferences get() = requireContext().sharedPreferences

fun DialogFragment.show(context: Context, tag: String? = null) {
context.getActivity()?.let {
show(it.supportFragmentManager, tag)
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/zionhuang/music/playback/SongPlayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import com.zionhuang.music.constants.MediaSessionConstants.ACTION_LIKE
import com.zionhuang.music.constants.MediaSessionConstants.ACTION_REMOVE_FROM_LIBRARY
import com.zionhuang.music.constants.MediaSessionConstants.ACTION_TOGGLE_LIBRARY
import com.zionhuang.music.constants.MediaSessionConstants.ACTION_TOGGLE_LIKE
import com.zionhuang.music.constants.MediaSessionConstants.ACTION_TOGGLE_SHUFFLE
import com.zionhuang.music.constants.MediaSessionConstants.ACTION_UNLIKE
import com.zionhuang.music.constants.MediaSessionConstants.COMMAND_ADD_TO_QUEUE
import com.zionhuang.music.constants.MediaSessionConstants.COMMAND_PLAY_NEXT
Expand Down Expand Up @@ -275,6 +276,18 @@ class SongPlayer(
if (currentSong != null) R.drawable.ic_library_add_check else R.drawable.ic_library_add
).build()
} else null
},
object : MediaSessionConnector.CustomActionProvider {
override fun onCustomAction(player: Player, action: String, extras: Bundle?) {
player.shuffleModeEnabled = !player.shuffleModeEnabled
}

override fun getCustomAction(player: Player) =
CustomAction.Builder(
ACTION_TOGGLE_SHUFFLE,
context.getString(R.string.btn_shuffle),
if (player.shuffleModeEnabled) R.drawable.ic_shuffle_on else R.drawable.ic_shuffle
).build()
}
)
setQueueNavigator { player, windowIndex -> player.getMediaItemAt(windowIndex).metadata!!.toMediaDescription(context) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ class QueueSheetFragment : Fragment() {
mainActivity.queueSheetBehavior.state = STATE_COLLAPSED
}
binding.btnLyrics.setOnClickListener {
val sharedPreferences = requireContext().sharedPreferences
sharedPreferences.edit {
putBoolean(getString(R.string.pref_show_lyrics), !sharedPreferences.getBoolean(getString(R.string.pref_show_lyrics), false))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.zionhuang.music.ui.fragments

import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.webkit.CookieManager
import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.content.edit
import com.zionhuang.innertube.YouTube
import com.zionhuang.music.constants.Constants.ACCOUNT_EMAIL
import com.zionhuang.music.constants.Constants.ACCOUNT_NAME
import com.zionhuang.music.constants.Constants.INNERTUBE_COOKIE
import com.zionhuang.music.constants.Constants.VISITOR_DATA
import com.zionhuang.music.databinding.FragmentWebviewBinding
import com.zionhuang.music.extensions.sharedPreferences
import com.zionhuang.music.ui.fragments.base.BindingFragment
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

class WebViewFragment : BindingFragment<FragmentWebviewBinding>() {
override fun getViewBinding() = FragmentWebviewBinding.inflate(layoutInflater)

@OptIn(DelicateCoroutinesApi::class)
private val webViewClient = object : WebViewClient() {
override fun doUpdateVisitedHistory(view: WebView, url: String, isReload: Boolean) {
if (url.startsWith("https://music.youtube.com")) {
val cookies = CookieManager.getInstance().getCookie(url)
if (sharedPreferences.getString(INNERTUBE_COOKIE, null) != cookies) {
sharedPreferences.edit {
putString(INNERTUBE_COOKIE, cookies)
}
GlobalScope.launch {
YouTube.getAccountInfo().onSuccess {
sharedPreferences.edit {
putString(ACCOUNT_NAME, it?.name)
putString(ACCOUNT_EMAIL, it?.email)
}
}.onFailure {
it.printStackTrace()
}
}
}
}
}

override fun onPageFinished(view: WebView, url: String?) {
binding.webview.loadUrl("javascript:Android.onRetrieveVisitorData(window.yt.config_.VISITOR_DATA)")
}
}

@SuppressLint("SetJavaScriptEnabled")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.webview.apply {
if (savedInstanceState != null) {
restoreState(savedInstanceState)
} else {
loadUrl("https://accounts.google.com/ServiceLogin?ltmpl=music&service=youtube&passive=true&continue=https%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26next%3Dhttps%253A%252F%252Fmusic.youtube.com%252F")
}
webViewClient = this@WebViewFragment.webViewClient
settings.apply {
javaScriptEnabled = true
setSupportZoom(true)
builtInZoomControls = true
}
addJavascriptInterface(this@WebViewFragment, "Android")
}
}

@JavascriptInterface
fun onRetrieveVisitorData(visitorData: String?) {
if (visitorData != null && sharedPreferences.getString(VISITOR_DATA, null) != visitorData) {
sharedPreferences.edit {
putString(VISITOR_DATA, visitorData)
}
}
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
binding.webview.saveState(outState)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ abstract class BaseSettingsFragment : PreferenceFragmentCompat() {
}
is EditTextPreference -> {
val binding = DialogEditTextPreferenceBinding.inflate(layoutInflater)
binding.editText.setText(requireContext().sharedPreferences.getString(preference.key, ""))
binding.editText.setText(sharedPreferences.getString(preference.key, ""))
MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.title)
.setView(binding.root)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
package com.zionhuang.music.ui.fragments.settings

import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import android.view.View
import androidx.navigation.fragment.findNavController
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
import com.zionhuang.music.R
import com.zionhuang.music.constants.Constants.ACCOUNT_EMAIL
import com.zionhuang.music.constants.Constants.ACCOUNT_NAME
import com.zionhuang.music.extensions.preferenceLiveData
import com.zionhuang.music.extensions.sharedPreferences
import com.zionhuang.music.ui.activities.MainActivity
import com.zionhuang.music.ui.fragments.base.BaseSettingsFragment
import kotlin.system.exitProcess

class ContentSettingsFragment : BaseSettingsFragment() {
class ContentSettingsFragment : BaseSettingsFragment(), OnSharedPreferenceChangeListener {
private lateinit var accountPreference: Preference
private lateinit var proxyTypePreference: ListPreference
private lateinit var proxyUrlPreference: EditTextPreference

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_content)

accountPreference = findPreference<Preference>(getString(R.string.pref_account))!!.apply {
title = sharedPreferences?.getString(ACCOUNT_NAME, null) ?: getString(R.string.login)
summary = sharedPreferences?.getString(ACCOUNT_EMAIL, null).orEmpty()
setOnPreferenceClickListener {
findNavController().navigate(R.id.webviewFragment)
true
}
}
val proxyEnabledPreference = findPreference<SwitchPreferenceCompat>(getString(R.string.pref_proxy_enabled))!!
proxyTypePreference = findPreference<ListPreference>(getString(R.string.pref_proxy_type))!!.apply {
isVisible = proxyEnabledPreference.isChecked
Expand All @@ -39,5 +54,18 @@ class ContentSettingsFragment : BaseSettingsFragment() {
proxyTypePreference.isVisible = it
proxyUrlPreference.isVisible = it
}
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
ACCOUNT_NAME -> accountPreference.title = sharedPreferences.getString(ACCOUNT_NAME, null) ?: getString(R.string.login)
ACCOUNT_EMAIL -> accountPreference.summary = sharedPreferences.getString(ACCOUNT_EMAIL, null).orEmpty()
}
}

override fun onDestroy() {
super.onDestroy()
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class YouTubeSuggestionFragment : NavigationFragment<FragmentYoutubeSuggestionBi

@OptIn(DelicateCoroutinesApi::class)
private fun search(query: String) {
if (!requireContext().sharedPreferences.getBoolean(getString(R.string.pref_pause_search_history), false)) {
if (!sharedPreferences.getBoolean(getString(R.string.pref_pause_search_history), false)) {
GlobalScope.launch {
SongRepository(requireContext()).insertSearchHistory(query)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class YouTubeHeaderViewHolder(
) : YouTubeViewHolder<ItemYoutubeHeaderBinding>(viewGroup, R.layout.item_youtube_header) {
fun bind(header: Header) {
binding.header = header
binding.root.isClickable = header.moreNavigationEndpoint != null
binding.root.isEnabled = header.moreNavigationEndpoint != null
if (header.moreNavigationEndpoint != null) {
binding.root.setOnClickListener {
navigationEndpointHandler.handle(header.moreNavigationEndpoint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import com.zionhuang.music.utils.livedata.SafeLiveData

open class PreferenceLiveData<T : Any>(
context: Context,
@StringRes private val keyId: Int,
val key: String,
private val defValue: T,
) : SafeLiveData<T>(defValue) {
protected val sharedPreferences: SharedPreferences = context.sharedPreferences
protected val key = context.getString(keyId)

constructor(context: Context, @StringRes keyId: Int, defValue: T) : this(context, context.getString(keyId), defValue)

protected fun getPreferenceValue() = sharedPreferences.get(key, defValue)

Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_person.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector>
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_shuffle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?invertedColor"
android:fillColor="@android:color/white"
android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z" />
</vector>
11 changes: 11 additions & 0 deletions app/src/main/res/drawable/ic_shuffle_on.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:fillType="evenOdd"
android:pathData="M21,1L3,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,3c0,-1.1 -0.9,-2 -2,-2zM10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z" />
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/layout/fragment_webview.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3 changes: 3 additions & 0 deletions app/src/main/res/navigation/settings_navigation_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@
android:id="@+id/aboutFragment"
android:name="com.zionhuang.music.ui.fragments.settings.AboutFragment"
android:label="@string/pref_about_title" />
<fragment
android:id="@+id/webviewFragment"
android:name="com.zionhuang.music.ui.fragments.WebViewFragment" />
</navigation>
Loading

0 comments on commit af7bd0f

Please sign in to comment.