mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-06-05 01:53:05 +03:00
Merge branch 'search' into 'develop'
Move search button in to main Topbar menu Closes #1247 See merge request ultrasonic/ultrasonic!1048
This commit is contained in:
commit
971ed705fa
@ -2,7 +2,7 @@
|
|||||||
# You need to run ./gradlew wrapper after updating the version
|
# You need to run ./gradlew wrapper after updating the version
|
||||||
gradle = "8.1.1"
|
gradle = "8.1.1"
|
||||||
|
|
||||||
navigation = "2.5.3"
|
navigation = "2.6.0"
|
||||||
gradlePlugin = "8.0.2"
|
gradlePlugin = "8.0.2"
|
||||||
androidxcore = "1.10.1"
|
androidxcore = "1.10.1"
|
||||||
ktlint = "0.43.2"
|
ktlint = "0.43.2"
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of Subsonic.
|
|
||||||
|
|
||||||
Subsonic is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Subsonic is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Copyright 2010 (C) Sindre Mehus
|
|
||||||
*/
|
|
||||||
package org.moire.ultrasonic.provider;
|
|
||||||
|
|
||||||
import android.content.SearchRecentSuggestionsProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides search suggestions based on recent searches.
|
|
||||||
*
|
|
||||||
* @author Sindre Mehus
|
|
||||||
*/
|
|
||||||
public class SearchSuggestionProvider extends SearchRecentSuggestionsProvider
|
|
||||||
{
|
|
||||||
public static final String AUTHORITY = SearchSuggestionProvider.class.getName();
|
|
||||||
public static final int MODE = DATABASE_MODE_QUERIES;
|
|
||||||
|
|
||||||
public SearchSuggestionProvider()
|
|
||||||
{
|
|
||||||
setupSuggestions(AUTHORITY, MODE);
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,16 +18,20 @@ import android.os.Bundle
|
|||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.SearchRecentSuggestions
|
import android.provider.SearchRecentSuggestions
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
|
import androidx.core.view.MenuProvider
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.fragment.app.FragmentContainerView
|
import androidx.fragment.app.FragmentContainerView
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player.STATE_BUFFERING
|
import androidx.media3.common.Player.STATE_BUFFERING
|
||||||
@ -94,6 +98,10 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
private var selectServerButton: MaterialButton? = null
|
private var selectServerButton: MaterialButton? = null
|
||||||
private var headerBackgroundImage: ImageView? = null
|
private var headerBackgroundImage: ImageView? = null
|
||||||
|
|
||||||
|
// We store the last search string in this variable.
|
||||||
|
// Seems a bit like a hack, is there a better way?
|
||||||
|
var searchQuery: String? = null
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
|
|
||||||
private var rxBusSubscription: CompositeDisposable = CompositeDisposable()
|
private var rxBusSubscription: CompositeDisposable = CompositeDisposable()
|
||||||
@ -221,10 +229,57 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && !UApp.instance!!.isFirstRun) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && !UApp.instance!!.isFirstRun) {
|
||||||
ShortcutUtil.registerShortcuts(this)
|
ShortcutUtil.registerShortcuts(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register our options menu
|
||||||
|
addMenuProvider(
|
||||||
|
searchMenuProvider,
|
||||||
|
this,
|
||||||
|
Lifecycle.State.RESUMED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val searchMenuProvider: MenuProvider = object : MenuProvider {
|
||||||
|
override fun onPrepareMenu(menu: Menu) {
|
||||||
|
setupSearchField(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.search_view_menu, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupSearchField(menu: Menu) {
|
||||||
|
Timber.i("Recreating search field")
|
||||||
|
val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
||||||
|
val searchItem = menu.findItem(R.id.action_search)
|
||||||
|
val searchView = searchItem.actionView as SearchView
|
||||||
|
val searchableInfo = searchManager.getSearchableInfo(this.componentName)
|
||||||
|
searchView.setSearchableInfo(searchableInfo)
|
||||||
|
searchView.setIconifiedByDefault(false)
|
||||||
|
|
||||||
|
if (searchQuery != null) {
|
||||||
|
Timber.e("Found existing search query")
|
||||||
|
searchItem.expandActionView()
|
||||||
|
searchView.isIconified = false
|
||||||
|
searchView.setQuery(searchQuery, false)
|
||||||
|
searchView.clearFocus()
|
||||||
|
// Restore search text only once!
|
||||||
|
searchQuery = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupDrawerLayout(drawerLayout: DrawerLayout) {
|
private fun setupDrawerLayout(drawerLayout: DrawerLayout) {
|
||||||
|
// Set initial state passed on drawer state
|
||||||
|
closeNavigationDrawerOnBack.isEnabled = drawerLayout.isOpen
|
||||||
|
|
||||||
|
// Add the back press listener
|
||||||
onBackPressedDispatcher.addCallback(this, closeNavigationDrawerOnBack)
|
onBackPressedDispatcher.addCallback(this, closeNavigationDrawerOnBack)
|
||||||
|
|
||||||
|
// Listen to changes in the drawer state and enable the back press listener accordingly.
|
||||||
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
|
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
|
||||||
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
||||||
// Nothing
|
// Nothing
|
||||||
@ -392,47 +447,55 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
|
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test if this works with external Intents
|
|
||||||
// android.intent.action.SEARCH and android.media.action.MEDIA_PLAY_FROM_SEARCH calls here
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
if (intent == null) return
|
|
||||||
|
|
||||||
if (intent.action == Constants.INTENT_PLAY_RANDOM_SONGS) {
|
when (intent?.action) {
|
||||||
val currentFragment = host?.childFragmentManager?.fragments?.last() ?: return
|
Constants.INTENT_PLAY_RANDOM_SONGS -> {
|
||||||
val service = MusicServiceFactory.getMusicService()
|
playRandomSongs()
|
||||||
val musicDirectory = service.getRandomSongs(Settings.maxSongs)
|
}
|
||||||
val downloadHandler: DownloadHandler by inject()
|
Intent.ACTION_MAIN -> {
|
||||||
downloadHandler.addTracksToMediaController(
|
if (intent.getBooleanExtra(Constants.INTENT_SHOW_PLAYER, false)) {
|
||||||
songs = musicDirectory.getTracks(),
|
findNavController(R.id.nav_host_fragment).navigate(R.id.playerFragment)
|
||||||
append = false,
|
}
|
||||||
playNext = false,
|
}
|
||||||
autoPlay = true,
|
Intent.ACTION_SEARCH -> {
|
||||||
shuffle = false,
|
searchQuery = intent.getStringExtra(SearchManager.QUERY)
|
||||||
fragment = currentFragment,
|
handleSearchIntent(searchQuery, false)
|
||||||
playlistName = null
|
}
|
||||||
)
|
MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH -> {
|
||||||
return
|
searchQuery = intent.getStringExtra(SearchManager.QUERY)
|
||||||
|
handleSearchIntent(searchQuery, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (intent.getBooleanExtra(Constants.INTENT_SHOW_PLAYER, false)) {
|
private fun handleSearchIntent(query: String?, autoPlay: Boolean) {
|
||||||
findNavController(R.id.nav_host_fragment).navigate(R.id.playerFragment)
|
val suggestions = SearchRecentSuggestions(
|
||||||
return
|
this,
|
||||||
}
|
SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE
|
||||||
|
)
|
||||||
|
suggestions.saveRecentQuery(query, null)
|
||||||
|
|
||||||
val query = intent.getStringExtra(SearchManager.QUERY)
|
val action = NavigationGraphDirections.toSearchFragment(query, autoPlay)
|
||||||
|
findNavController(R.id.nav_host_fragment).navigate(action)
|
||||||
|
}
|
||||||
|
|
||||||
if (query != null) {
|
private fun playRandomSongs() {
|
||||||
val autoPlay = intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH
|
val currentFragment = host?.childFragmentManager?.fragments?.last() ?: return
|
||||||
val suggestions = SearchRecentSuggestions(
|
val service = MusicServiceFactory.getMusicService()
|
||||||
this,
|
val musicDirectory = service.getRandomSongs(Settings.maxSongs)
|
||||||
SearchSuggestionProvider.AUTHORITY, SearchSuggestionProvider.MODE
|
val downloadHandler: DownloadHandler by inject()
|
||||||
)
|
downloadHandler.addTracksToMediaController(
|
||||||
suggestions.saveRecentQuery(query, null)
|
songs = musicDirectory.getTracks(),
|
||||||
|
append = false,
|
||||||
val action = NavigationGraphDirections.toSearchFragment(query, autoPlay)
|
playNext = false,
|
||||||
findNavController(R.id.nav_host_fragment).navigate(action)
|
autoPlay = true,
|
||||||
}
|
shuffle = false,
|
||||||
|
fragment = currentFragment,
|
||||||
|
playlistName = null
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,19 +7,11 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.fragment
|
package org.moire.ultrasonic.fragment
|
||||||
|
|
||||||
import android.app.SearchManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.widget.SearchView
|
|
||||||
import androidx.core.view.MenuHost
|
|
||||||
import androidx.core.view.MenuProvider
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
@ -54,16 +46,14 @@ import org.moire.ultrasonic.util.CommunicationError
|
|||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import org.moire.ultrasonic.util.Util.toast
|
import org.moire.ultrasonic.util.Util.toast
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates a search on the media library and displays the results
|
* Initiates a search on the media library and displays the results
|
||||||
* TODO: Switch to material3 class
|
|
||||||
*/
|
*/
|
||||||
class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||||
private var searchResult: SearchResult? = null
|
private var searchResult: SearchResult? = null
|
||||||
private var searchRefresh: SwipeRefreshLayout? = null
|
private var searchRefresh: SwipeRefreshLayout? = null
|
||||||
private var searchView: SearchView? = null
|
|
||||||
|
|
||||||
private val mediaPlayerManager: MediaPlayerManager by inject()
|
private val mediaPlayerManager: MediaPlayerManager by inject()
|
||||||
|
|
||||||
@ -83,13 +73,6 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
|||||||
cancellationToken = CancellationToken()
|
cancellationToken = CancellationToken()
|
||||||
setTitle(this, R.string.search_title)
|
setTitle(this, R.string.search_title)
|
||||||
|
|
||||||
// Register our options menu
|
|
||||||
(requireActivity() as MenuHost).addMenuProvider(
|
|
||||||
menuProvider,
|
|
||||||
viewLifecycleOwner,
|
|
||||||
Lifecycle.State.RESUMED
|
|
||||||
)
|
|
||||||
|
|
||||||
listModel.searchResult.observe(
|
listModel.searchResult.observe(
|
||||||
viewLifecycleOwner
|
viewLifecycleOwner
|
||||||
) {
|
) {
|
||||||
@ -148,73 +131,6 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This provide creates the search bar above the recycler view
|
|
||||||
*/
|
|
||||||
private val menuProvider: MenuProvider = object : MenuProvider {
|
|
||||||
override fun onPrepareMenu(menu: Menu) {
|
|
||||||
setupOptionsMenu(menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
|
||||||
menuInflater.inflate(R.menu.search, menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun setupOptionsMenu(menu: Menu) {
|
|
||||||
val activity = activity ?: return
|
|
||||||
val searchManager = activity.getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
|
||||||
val searchItem = menu.findItem(R.id.search_item)
|
|
||||||
searchView = searchItem.actionView as SearchView
|
|
||||||
val searchableInfo = searchManager.getSearchableInfo(requireActivity().componentName)
|
|
||||||
searchView!!.setSearchableInfo(searchableInfo)
|
|
||||||
|
|
||||||
val autoPlay = navArgs.autoplay
|
|
||||||
val query = navArgs.query
|
|
||||||
|
|
||||||
// If started with a query, enter it to the searchView
|
|
||||||
if (query != null) {
|
|
||||||
searchView!!.setQuery(query, false)
|
|
||||||
searchView!!.clearFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
searchView!!.setOnSuggestionListener(object : SearchView.OnSuggestionListener {
|
|
||||||
override fun onSuggestionSelect(position: Int): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSuggestionClick(position: Int): Boolean {
|
|
||||||
Timber.d("onSuggestionClick: %d", position)
|
|
||||||
val cursor = searchView!!.suggestionsAdapter.cursor
|
|
||||||
cursor.moveToPosition(position)
|
|
||||||
|
|
||||||
// 2 is the index of col containing suggestion name.
|
|
||||||
val suggestion = cursor.getString(2)
|
|
||||||
searchView!!.setQuery(suggestion, true)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
searchView!!.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
|
||||||
override fun onQueryTextSubmit(query: String): Boolean {
|
|
||||||
Timber.d("onQueryTextSubmit: %s", query)
|
|
||||||
searchView!!.clearFocus()
|
|
||||||
search(query, autoPlay)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
searchView!!.setIconifiedByDefault(false)
|
|
||||||
searchItem.expandActionView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
Util.hideKeyboard(activity)
|
Util.hideKeyboard(activity)
|
||||||
cancellationToken?.cancel()
|
cancellationToken?.cancel()
|
||||||
@ -313,7 +229,6 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun onAlbumSelected(album: Album, autoplay: Boolean) {
|
private fun onAlbumSelected(album: Album, autoplay: Boolean) {
|
||||||
|
|
||||||
val action = SearchFragmentDirections.searchToTrackCollection(
|
val action = SearchFragmentDirections.searchToTrackCollection(
|
||||||
id = album.id,
|
id = album.id,
|
||||||
name = album.title,
|
name = album.title,
|
||||||
|
@ -36,7 +36,6 @@ import org.moire.ultrasonic.adapters.AlbumHeader
|
|||||||
import org.moire.ultrasonic.adapters.AlbumRowDelegate
|
import org.moire.ultrasonic.adapters.AlbumRowDelegate
|
||||||
import org.moire.ultrasonic.adapters.HeaderViewBinder
|
import org.moire.ultrasonic.adapters.HeaderViewBinder
|
||||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||||
import org.moire.ultrasonic.domain.Identifiable
|
import org.moire.ultrasonic.domain.Identifiable
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
@ -64,7 +63,6 @@ import timber.log.Timber
|
|||||||
* In most cases the data should be just a list of Entries, but there are some cases
|
* In most cases the data should be just a list of Entries, but there are some cases
|
||||||
* where the list can contain Albums as well. This happens especially when having ID3 tags disabled,
|
* where the list can contain Albums as well. This happens especially when having ID3 tags disabled,
|
||||||
* or using Offline mode, both in which Indexes instead of Artists are being used.
|
* or using Offline mode, both in which Indexes instead of Artists are being used.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
open class TrackCollectionFragment(
|
open class TrackCollectionFragment(
|
||||||
@ -268,6 +266,9 @@ open class TrackCollectionFragment(
|
|||||||
|
|
||||||
private val menuProvider: MenuProvider = object : MenuProvider {
|
private val menuProvider: MenuProvider = object : MenuProvider {
|
||||||
override fun onPrepareMenu(menu: Menu) {
|
override fun onPrepareMenu(menu: Menu) {
|
||||||
|
// Hide search button (from xml)
|
||||||
|
menu.findItem(R.id.action_search).isVisible = false
|
||||||
|
|
||||||
playAllButton = menu.findItem(R.id.select_album_play_all)
|
playAllButton = menu.findItem(R.id.select_album_play_all)
|
||||||
|
|
||||||
if (playAllButton != null) {
|
if (playAllButton != null) {
|
||||||
@ -282,7 +283,7 @@ open class TrackCollectionFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.select_album, menu)
|
inflater.inflate(R.menu.track_collection_menu, menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
override fun onMenuItemSelected(item: MenuItem): Boolean {
|
||||||
@ -584,12 +585,8 @@ open class TrackCollectionFragment(
|
|||||||
} else {
|
} else {
|
||||||
setTitle(name)
|
setTitle(name)
|
||||||
|
|
||||||
if (ActiveServerProvider.shouldUseId3Tags()) {
|
if (isAlbum) {
|
||||||
if (isAlbum) {
|
listModel.getAlbum(refresh2, id, name)
|
||||||
listModel.getAlbum(refresh2, id, name)
|
|
||||||
} else {
|
|
||||||
throw IllegalAccessException("Use AlbumFragment instead!")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
listModel.getMusicDirectory(refresh2, id, name)
|
listModel.getMusicDirectory(refresh2, id, name)
|
||||||
}
|
}
|
||||||
|
@ -340,6 +340,7 @@ class PlaybackService :
|
|||||||
// needed starting Android 12 (S = 31)
|
// needed starting Android 12 (S = 31)
|
||||||
flags = flags or FLAG_IMMUTABLE
|
flags = flags or FLAG_IMMUTABLE
|
||||||
}
|
}
|
||||||
|
intent.action = Intent.ACTION_MAIN
|
||||||
intent.putExtra(Constants.INTENT_SHOW_PLAYER, true)
|
intent.putExtra(Constants.INTENT_SHOW_PLAYER, true)
|
||||||
return PendingIntent.getActivity(this, 0, intent, flags)
|
return PendingIntent.getActivity(this, 0, intent, flags)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SearchSuggestionProvider.kt
|
||||||
|
* Copyright (C) 2009-2023 Ultrasonic developers
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
|
*/
|
||||||
|
package org.moire.ultrasonic.provider
|
||||||
|
|
||||||
|
import android.content.SearchRecentSuggestionsProvider
|
||||||
|
import org.moire.ultrasonic.BuildConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides search suggestions based on recent searches.
|
||||||
|
*/
|
||||||
|
class SearchSuggestionProvider : SearchRecentSuggestionsProvider() {
|
||||||
|
init {
|
||||||
|
setupSuggestions(AUTHORITY, MODE)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val AUTHORITY = "${BuildConfig.APPLICATION_ID}.provider.SearchSuggestionProvider"
|
||||||
|
const val MODE = DATABASE_MODE_QUERIES
|
||||||
|
}
|
||||||
|
}
|
@ -219,8 +219,8 @@ open class UltrasonicAppWidgetProvider : AppWidgetProvider() {
|
|||||||
NavigationActivity::class.java
|
NavigationActivity::class.java
|
||||||
).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
if (playerActive) intent.putExtra(Constants.INTENT_SHOW_PLAYER, true)
|
if (playerActive) intent.putExtra(Constants.INTENT_SHOW_PLAYER, true)
|
||||||
intent.action = "android.intent.action.MAIN"
|
intent.action = Intent.ACTION_MAIN
|
||||||
intent.addCategory("android.intent.category.LAUNCHER")
|
intent.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
var flags = PendingIntent.FLAG_UPDATE_CURRENT
|
var flags = PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
// needed starting Android 12 (S = 31)
|
// needed starting Android 12 (S = 31)
|
||||||
|
@ -30,7 +30,6 @@ object Constants {
|
|||||||
// Legacy Preferences keys
|
// Legacy Preferences keys
|
||||||
// Warning: Don't add any new here!
|
// Warning: Don't add any new here!
|
||||||
// Use setting_keys.xml
|
// Use setting_keys.xml
|
||||||
const val PREFERENCES_KEY_USE_FIVE_STAR_RATING = "use_five_star_rating"
|
|
||||||
const val PREFERENCE_VALUE_ALL = 0
|
const val PREFERENCE_VALUE_ALL = 0
|
||||||
const val PREFERENCE_VALUE_A2DP = 1
|
const val PREFERENCE_VALUE_A2DP = 1
|
||||||
const val PREFERENCE_VALUE_DISABLED = 2
|
const val PREFERENCE_VALUE_DISABLED = 2
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:a="http://schemas.android.com/apk/res/android">
|
|
||||||
<item a:id="@+id/search_item"
|
|
||||||
a:title="@string/search.label"
|
|
||||||
a:icon="@drawable/ic_menu_search"
|
|
||||||
app:showAsAction="always"
|
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView" />
|
|
||||||
</menu>
|
|
12
ultrasonic/src/main/res/menu/search_view_menu.xml
Normal file
12
ultrasonic/src/main/res/menu/search_view_menu.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:a="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
a:id="@+id/action_search"
|
||||||
|
a:icon="@drawable/ic_menu_search"
|
||||||
|
a:title="@string/button_bar.search"
|
||||||
|
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||||
|
app:showAsAction="always|collapseActionView" />
|
||||||
|
|
||||||
|
</menu>
|
@ -104,7 +104,6 @@
|
|||||||
<string name="playlist.updated_info_error">Chyba aktualizace informací playlistu %s</string>
|
<string name="playlist.updated_info_error">Chyba aktualizace informací playlistu %s</string>
|
||||||
<string name="search.albums">Alba</string>
|
<string name="search.albums">Alba</string>
|
||||||
<string name="search.artists">Umělci</string>
|
<string name="search.artists">Umělci</string>
|
||||||
<string name="search.label">Vyhledávání</string>
|
|
||||||
<string name="search.more">Zobrazit více</string>
|
<string name="search.more">Zobrazit více</string>
|
||||||
<string name="search.no_match">Nenalezeno, zkuste znovu</string>
|
<string name="search.no_match">Nenalezeno, zkuste znovu</string>
|
||||||
<string name="search.songs">Skladby</string>
|
<string name="search.songs">Skladby</string>
|
||||||
|
@ -139,7 +139,6 @@
|
|||||||
<string name="playlist.updated_info_error">Aktualisierung der Wiedergabeliste %s ist fehlgeschlagen</string>
|
<string name="playlist.updated_info_error">Aktualisierung der Wiedergabeliste %s ist fehlgeschlagen</string>
|
||||||
<string name="search.albums">Alben</string>
|
<string name="search.albums">Alben</string>
|
||||||
<string name="search.artists">Künstler*innen</string>
|
<string name="search.artists">Künstler*innen</string>
|
||||||
<string name="search.label">Suche</string>
|
|
||||||
<string name="search.more">Zeige mehr</string>
|
<string name="search.more">Zeige mehr</string>
|
||||||
<string name="search.no_match">Keine Treffer, bitte erneut versuchen</string>
|
<string name="search.no_match">Keine Treffer, bitte erneut versuchen</string>
|
||||||
<string name="search.songs">Titel</string>
|
<string name="search.songs">Titel</string>
|
||||||
|
@ -141,7 +141,6 @@
|
|||||||
<string name="playlist.updated_info_error">Fallo al actualizar la información de la lista de reproducción para %s</string>
|
<string name="playlist.updated_info_error">Fallo al actualizar la información de la lista de reproducción para %s</string>
|
||||||
<string name="search.albums">Álbumes</string>
|
<string name="search.albums">Álbumes</string>
|
||||||
<string name="search.artists">Artistas</string>
|
<string name="search.artists">Artistas</string>
|
||||||
<string name="search.label">Buscar</string>
|
|
||||||
<string name="search.more">Mostrar mas</string>
|
<string name="search.more">Mostrar mas</string>
|
||||||
<string name="search.no_match">Sin resultados, por favor inténtalo de nuevo</string>
|
<string name="search.no_match">Sin resultados, por favor inténtalo de nuevo</string>
|
||||||
<string name="search.songs">Canciones</string>
|
<string name="search.songs">Canciones</string>
|
||||||
|
@ -137,7 +137,6 @@
|
|||||||
<string name="playlist.updated_info_error">Échec de la mise à jour des informations de la liste de lecture pour %s</string>
|
<string name="playlist.updated_info_error">Échec de la mise à jour des informations de la liste de lecture pour %s</string>
|
||||||
<string name="search.albums">Albums</string>
|
<string name="search.albums">Albums</string>
|
||||||
<string name="search.artists">Artistes</string>
|
<string name="search.artists">Artistes</string>
|
||||||
<string name="search.label">Recherche</string>
|
|
||||||
<string name="search.more">Afficher plus</string>
|
<string name="search.more">Afficher plus</string>
|
||||||
<string name="search.no_match">Aucun résultat, veuillez essayer à nouveau</string>
|
<string name="search.no_match">Aucun résultat, veuillez essayer à nouveau</string>
|
||||||
<string name="search.songs">Titres</string>
|
<string name="search.songs">Titres</string>
|
||||||
|
@ -110,7 +110,6 @@
|
|||||||
<string name="playlist.updated_info_error">Lejátszási lista módosítása sikertelen %s</string>
|
<string name="playlist.updated_info_error">Lejátszási lista módosítása sikertelen %s</string>
|
||||||
<string name="search.albums">Albumok</string>
|
<string name="search.albums">Albumok</string>
|
||||||
<string name="search.artists">Előadók</string>
|
<string name="search.artists">Előadók</string>
|
||||||
<string name="search.label">Keresés</string>
|
|
||||||
<string name="search.more">Továbbiak</string>
|
<string name="search.more">Továbbiak</string>
|
||||||
<string name="search.no_match">Nincs találat, próbálja újra!</string>
|
<string name="search.no_match">Nincs találat, próbálja újra!</string>
|
||||||
<string name="search.songs">Dalok</string>
|
<string name="search.songs">Dalok</string>
|
||||||
|
@ -100,7 +100,6 @@
|
|||||||
<string name="playlist.updated_info_error">Impossibile aggiornare informazioni playlist per %s</string>
|
<string name="playlist.updated_info_error">Impossibile aggiornare informazioni playlist per %s</string>
|
||||||
<string name="search.albums">Album</string>
|
<string name="search.albums">Album</string>
|
||||||
<string name="search.artists">Artisti</string>
|
<string name="search.artists">Artisti</string>
|
||||||
<string name="search.label">Cerca</string>
|
|
||||||
<string name="search.more">Mostra di più</string>
|
<string name="search.more">Mostra di più</string>
|
||||||
<string name="search.no_match">Nessun risultato, riprova per favore</string>
|
<string name="search.no_match">Nessun risultato, riprova per favore</string>
|
||||||
<string name="search.songs">Canzoni</string>
|
<string name="search.songs">Canzoni</string>
|
||||||
|
@ -106,7 +106,6 @@
|
|||||||
<string name="playlist.updated_info_error">%s のプレイリスト情報をアップデートできません</string>
|
<string name="playlist.updated_info_error">%s のプレイリスト情報をアップデートできません</string>
|
||||||
<string name="search.albums">アルバム</string>
|
<string name="search.albums">アルバム</string>
|
||||||
<string name="search.artists">アーティスト</string>
|
<string name="search.artists">アーティスト</string>
|
||||||
<string name="search.label">検索</string>
|
|
||||||
<string name="search.more">もっと表示</string>
|
<string name="search.more">もっと表示</string>
|
||||||
<string name="search.no_match">一致するものはありません、やり直してください</string>
|
<string name="search.no_match">一致するものはありません、やり直してください</string>
|
||||||
<string name="search.songs">曲</string>
|
<string name="search.songs">曲</string>
|
||||||
|
@ -186,7 +186,6 @@
|
|||||||
<string name="playlist.update_info">Oppdater info</string>
|
<string name="playlist.update_info">Oppdater info</string>
|
||||||
<string name="search.albums">Album</string>
|
<string name="search.albums">Album</string>
|
||||||
<string name="search.artists">Artister</string>
|
<string name="search.artists">Artister</string>
|
||||||
<string name="search.label">Søk</string>
|
|
||||||
<string name="search.more">Vis mer</string>
|
<string name="search.more">Vis mer</string>
|
||||||
<string name="search.songs">Spor</string>
|
<string name="search.songs">Spor</string>
|
||||||
<string name="search.title">Søk</string>
|
<string name="search.title">Søk</string>
|
||||||
|
@ -142,7 +142,6 @@
|
|||||||
<string name="playlist.updated_info_error">Kan afspeellijstinformatie voor %s niet bijwerken</string>
|
<string name="playlist.updated_info_error">Kan afspeellijstinformatie voor %s niet bijwerken</string>
|
||||||
<string name="search.albums">Albums</string>
|
<string name="search.albums">Albums</string>
|
||||||
<string name="search.artists">Artiesten</string>
|
<string name="search.artists">Artiesten</string>
|
||||||
<string name="search.label">Zoeken</string>
|
|
||||||
<string name="search.more">Meer tonen</string>
|
<string name="search.more">Meer tonen</string>
|
||||||
<string name="search.no_match">Geen overeenkomsten; probeer het opnieuw</string>
|
<string name="search.no_match">Geen overeenkomsten; probeer het opnieuw</string>
|
||||||
<string name="search.songs">Nummers</string>
|
<string name="search.songs">Nummers</string>
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
<string name="playlist.updated_info_error">Błąd podczas aktualizacji playlisty %s</string>
|
<string name="playlist.updated_info_error">Błąd podczas aktualizacji playlisty %s</string>
|
||||||
<string name="search.albums">Albumy</string>
|
<string name="search.albums">Albumy</string>
|
||||||
<string name="search.artists">Wykonawcy</string>
|
<string name="search.artists">Wykonawcy</string>
|
||||||
<string name="search.label">Wyszukaj</string>
|
|
||||||
<string name="search.more">Wyświetl więcej</string>
|
<string name="search.more">Wyświetl więcej</string>
|
||||||
<string name="search.no_match">Brak wyników, proszę spróbować ponownie</string>
|
<string name="search.no_match">Brak wyników, proszę spróbować ponownie</string>
|
||||||
<string name="search.songs">Utwory</string>
|
<string name="search.songs">Utwory</string>
|
||||||
|
@ -140,7 +140,6 @@
|
|||||||
<string name="playlist.updated_info_error">Falha ao atualizar a informação da playlist para %s</string>
|
<string name="playlist.updated_info_error">Falha ao atualizar a informação da playlist para %s</string>
|
||||||
<string name="search.albums">Álbuns</string>
|
<string name="search.albums">Álbuns</string>
|
||||||
<string name="search.artists">Artistas</string>
|
<string name="search.artists">Artistas</string>
|
||||||
<string name="search.label">Pesquisar</string>
|
|
||||||
<string name="search.more">Mostrar Mais</string>
|
<string name="search.more">Mostrar Mais</string>
|
||||||
<string name="search.no_match">Nada coincide, tente novamente</string>
|
<string name="search.no_match">Nada coincide, tente novamente</string>
|
||||||
<string name="search.songs">Músicas</string>
|
<string name="search.songs">Músicas</string>
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
<string name="playlist.updated_info_error">Falha ao atualizar a informação da playlist para %s</string>
|
<string name="playlist.updated_info_error">Falha ao atualizar a informação da playlist para %s</string>
|
||||||
<string name="search.albums">Álbuns</string>
|
<string name="search.albums">Álbuns</string>
|
||||||
<string name="search.artists">Artistas</string>
|
<string name="search.artists">Artistas</string>
|
||||||
<string name="search.label">Pesquisar</string>
|
|
||||||
<string name="search.more">Mostrar Mais</string>
|
<string name="search.more">Mostrar Mais</string>
|
||||||
<string name="search.no_match">Nada coincide, tente novamente</string>
|
<string name="search.no_match">Nada coincide, tente novamente</string>
|
||||||
<string name="search.songs">Músicas</string>
|
<string name="search.songs">Músicas</string>
|
||||||
|
@ -128,7 +128,6 @@
|
|||||||
<string name="playlist.updated_info_error">Не удалось обновить информацию о плейлисте для %s</string>
|
<string name="playlist.updated_info_error">Не удалось обновить информацию о плейлисте для %s</string>
|
||||||
<string name="search.albums">Альбомы</string>
|
<string name="search.albums">Альбомы</string>
|
||||||
<string name="search.artists">Исполнители</string>
|
<string name="search.artists">Исполнители</string>
|
||||||
<string name="search.label">Поиск</string>
|
|
||||||
<string name="search.more">Показать еще</string>
|
<string name="search.more">Показать еще</string>
|
||||||
<string name="search.no_match">Нет совпадений, пожалуйста попробуйте еще раз</string>
|
<string name="search.no_match">Нет совпадений, пожалуйста попробуйте еще раз</string>
|
||||||
<string name="search.songs">Песни</string>
|
<string name="search.songs">Песни</string>
|
||||||
|
@ -128,7 +128,6 @@
|
|||||||
<string name="playlist.updated_info_error">更新播放列表信息失败 - %s</string>
|
<string name="playlist.updated_info_error">更新播放列表信息失败 - %s</string>
|
||||||
<string name="search.albums">专辑</string>
|
<string name="search.albums">专辑</string>
|
||||||
<string name="search.artists">艺人</string>
|
<string name="search.artists">艺人</string>
|
||||||
<string name="search.label">搜索</string>
|
|
||||||
<string name="search.more">显示更多</string>
|
<string name="search.more">显示更多</string>
|
||||||
<string name="search.no_match">没有匹配的结果,请重试</string>
|
<string name="search.no_match">没有匹配的结果,请重试</string>
|
||||||
<string name="search.songs">歌曲</string>
|
<string name="search.songs">歌曲</string>
|
||||||
|
@ -67,7 +67,6 @@
|
|||||||
<string name="playlist.label">播放清單</string>
|
<string name="playlist.label">播放清單</string>
|
||||||
<string name="search.albums">專輯</string>
|
<string name="search.albums">專輯</string>
|
||||||
<string name="search.artists">歌手</string>
|
<string name="search.artists">歌手</string>
|
||||||
<string name="search.label">搜尋</string>
|
|
||||||
<string name="search.title">搜尋</string>
|
<string name="search.title">搜尋</string>
|
||||||
<string name="select_genre.empty">無符合類型</string>
|
<string name="select_genre.empty">無符合類型</string>
|
||||||
<string name="settings.increment_time_0">已停用</string>
|
<string name="settings.increment_time_0">已停用</string>
|
||||||
|
@ -142,7 +142,6 @@
|
|||||||
<string name="playlist.updated_info_error">Failed to update playlist information for %s</string>
|
<string name="playlist.updated_info_error">Failed to update playlist information for %s</string>
|
||||||
<string name="search.albums">Albums</string>
|
<string name="search.albums">Albums</string>
|
||||||
<string name="search.artists">Artists</string>
|
<string name="search.artists">Artists</string>
|
||||||
<string name="search.label">Search</string>
|
|
||||||
<string name="search.more">Show More</string>
|
<string name="search.more">Show More</string>
|
||||||
<string name="search.no_match">No matches, please try again</string>
|
<string name="search.no_match">No matches, please try again</string>
|
||||||
<string name="search.songs">Songs</string>
|
<string name="search.songs">Songs</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user