diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/MergeAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/MergeAdapter.java deleted file mode 100644 index 7d8880be..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/MergeAdapter.java +++ /dev/null @@ -1,316 +0,0 @@ -/** - Copyright (c) 2008-2009 CommonsWare, LLC - Portions (c) 2009 Google, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -package org.moire.ultrasonic.util; - -import android.database.DataSetObserver; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListAdapter; - -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Adapter that merges multiple child adapters and views - * into a single contiguous whole. - *

- * Adapters used as pieces within MergeAdapter must - * have view type IDs monotonically increasing from 0. Ideally, - * adapters also have distinct ranges for their row ids, as - * returned by getItemId(). - */ -public class MergeAdapter extends BaseAdapter -{ - private final CascadeDataSetObserver observer = new CascadeDataSetObserver(); - private final AbstractList pieces = new ArrayList(); - - /** - * Adds a new adapter to the roster of things to appear - * in the aggregate list. - * - * @param adapter Source for row views for this section - */ - public void addAdapter(ListAdapter adapter) - { - pieces.add(adapter); - adapter.registerDataSetObserver(observer); - } - - public void removeAdapter(ListAdapter adapter) - { - adapter.unregisterDataSetObserver(observer); - pieces.remove(adapter); - } - - /** - * Adds a new View to the roster of things to appear - * in the aggregate list. - * - * @param view Single view to add - */ - public ListAdapter addView(View view) - { - return addView(view, false); - } - - /** - * Adds a new View to the roster of things to appear - * in the aggregate list. - * - * @param view Single view to add - * @param enabled false if views are disabled, true if enabled - */ - public ListAdapter addView(View view, boolean enabled) - { - return addViews(Collections.singletonList(view), enabled); - } - - /** - * Adds a list of views to the roster of things to appear - * in the aggregate list. - * - * @param views List of views to add - */ - public ListAdapter addViews(List views) - { - return addViews(views, false); - } - - /** - * Adds a list of views to the roster of things to appear - * in the aggregate list. - * - * @param views List of views to add - * @param enabled false if views are disabled, true if enabled - */ - public ListAdapter addViews(List views, boolean enabled) - { - ListAdapter adapter = enabled ? new EnabledSackAdapter(views) : new SackOfViewsAdapter(views); - addAdapter(adapter); - return adapter; - } - - /** - * Get the data item associated with the specified - * position in the data set. - * - * @param position Position of the item whose data we want - */ - @Override - public Object getItem(int position) - { - for (ListAdapter piece : pieces) - { - int size = piece.getCount(); - - if (position < size) - { - return (piece.getItem(position)); - } - - position -= size; - } - - return (null); - } - - /** - * How many items are in the data set represented by this - * Adapter. - */ - @Override - public int getCount() - { - int total = 0; - - for (ListAdapter piece : pieces) - { - total += piece.getCount(); - } - - return (total); - } - - /** - * Returns the number of types of Views that will be - * created by getView(). - */ - @Override - public int getViewTypeCount() - { - int total = 0; - - for (ListAdapter piece : pieces) - { - total += piece.getViewTypeCount(); - } - - return (Math.max(total, 1)); // needed for setListAdapter() before content add' - } - - /** - * Get the type of View that will be created by getView() - * for the specified item. - * - * @param position Position of the item whose data we want - */ - @Override - public int getItemViewType(int position) - { - int typeOffset = 0; - int result = -1; - - for (ListAdapter piece : pieces) - { - int size = piece.getCount(); - - if (position < size) - { - result = typeOffset + piece.getItemViewType(position); - break; - } - - position -= size; - typeOffset += piece.getViewTypeCount(); - } - - return (result); - } - - /** - * Are all items in this ListAdapter enabled? If yes it - * means all items are selectable and clickable. - */ - @Override - public boolean areAllItemsEnabled() - { - return (false); - } - - /** - * Returns true if the item at the specified position is - * not a separator. - * - * @param position Position of the item whose data we want - */ - @Override - public boolean isEnabled(int position) - { - for (ListAdapter piece : pieces) - { - int size = piece.getCount(); - - if (position < size) - { - return (piece.isEnabled(position)); - } - - position -= size; - } - - return (false); - } - - /** - * Get a View that displays the data at the specified - * position in the data set. - * - * @param position Position of the item whose data we want - * @param convertView View to recycle, if not null - * @param parent ViewGroup containing the returned View - */ - @Override - public View getView(int position, View convertView, ViewGroup parent) - { - for (ListAdapter piece : pieces) - { - int size = piece.getCount(); - - if (position < size) - { - - return (piece.getView(position, convertView, parent)); - } - - position -= size; - } - - return (null); - } - - /** - * Get the row id associated with the specified position - * in the list. - * - * @param position Position of the item whose data we want - */ - @Override - public long getItemId(int position) - { - for (ListAdapter piece : pieces) - { - int size = piece.getCount(); - - if (position < size) - { - return (piece.getItemId(position)); - } - - position -= size; - } - - return (-1); - } - - private static class EnabledSackAdapter extends SackOfViewsAdapter - { - public EnabledSackAdapter(List views) - { - super(views); - } - - @Override - public boolean areAllItemsEnabled() - { - return (true); - } - - @Override - public boolean isEnabled(int position) - { - return (true); - } - } - - private class CascadeDataSetObserver extends DataSetObserver - { - @Override - public void onChanged() - { - notifyDataSetChanged(); - } - - @Override - public void onInvalidated() - { - notifyDataSetInvalidated(); - } - } -} - diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/SackOfViewsAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/SackOfViewsAdapter.java deleted file mode 100644 index 080e1430..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/SackOfViewsAdapter.java +++ /dev/null @@ -1,195 +0,0 @@ -/** - Copyright (c) 2008-2009 CommonsWare, LLC - Portions (c) 2009 Google, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); you may - not use this file except in compliance with the License. You may obtain - a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -package org.moire.ultrasonic.util; - -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListView; - -import java.util.ArrayList; -import java.util.List; - -/** - * Adapter that simply returns row views from a list. - *

- * If you supply a size, you must implement newView(), to - * create a required view. The adapter will then cache these - * views. - *

- * If you supply a list of views in the constructor, that - * list will be used directly. If any elements in the list - * are null, then newView() will be called just for those - * slots. - *

- * Subclasses may also wish to override areAllItemsEnabled() - * (default: false) and isEnabled() (default: false), if some - * of their rows should be selectable. - *

- * It is assumed each view is unique, and therefore will not - * get recycled. - *

- * Note that this adapter is not designed for long lists. It - * is more for screens that should behave like a list. This - * is particularly useful if you combine this with other - * adapters (e.g., SectionedAdapter) that might have an - * arbitrary number of rows, so it all appears seamless. - */ -public class SackOfViewsAdapter extends BaseAdapter -{ - private List views; - - /** - * Constructor creating an empty list of views, but with - * a specified count. Subclasses must override newView(). - */ - public SackOfViewsAdapter(int count) - { - super(); - - views = new ArrayList(count); - - for (int i = 0; i < count; i++) - { - views.add(null); - } - } - - /** - * Constructor wrapping a supplied list of views. - * Subclasses must override newView() if any of the elements - * in the list are null. - */ - public SackOfViewsAdapter(List views) - { - for (View view : views) - { - view.setLayoutParams(new ListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - } - this.views = views; - } - - /** - * Get the data item associated with the specified - * position in the data set. - * - * @param position Position of the item whose data we want - */ - @Override - public Object getItem(int position) - { - return (views.get(position)); - } - - /** - * How many items are in the data set represented by this - * Adapter. - */ - @Override - public int getCount() - { - return (views.size()); - } - - /** - * Returns the number of types of Views that will be - * created by getView(). - */ - @Override - public int getViewTypeCount() - { - return (getCount()); - } - - /** - * Get the type of View that will be created by getView() - * for the specified item. - * - * @param position Position of the item whose data we want - */ - @Override - public int getItemViewType(int position) - { - return (position); - } - - /** - * Are all items in this ListAdapter enabled? If yes it - * means all items are selectable and clickable. - */ - @Override - public boolean areAllItemsEnabled() - { - return (false); - } - - /** - * Returns true if the item at the specified position is - * not a separator. - * - * @param position Position of the item whose data we want - */ - @Override - public boolean isEnabled(int position) - { - return (false); - } - - /** - * Get a View that displays the data at the specified - * position in the data set. - * - * @param position Position of the item whose data we want - * @param convertView View to recycle, if not null - * @param parent ViewGroup containing the returned View - */ - @Override - public View getView(int position, View convertView, ViewGroup parent) - { - View result = views.get(position); - - if (result == null) - { - result = newView(position, parent); - views.set(position, result); - } - - return (result); - } - - /** - * Get the row id associated with the specified position - * in the list. - * - * @param position Position of the item whose data we want - */ - @Override - public long getItemId(int position) - { - return (position); - } - - /** - * Create a new View to go into the list at the specified - * position. - * - * @param position Position of the item whose data we want - * @param parent ViewGroup containing the returned View - */ - protected static View newView(int position, ViewGroup parent) - { - throw new RuntimeException("You must override newView()!"); - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index 44b67078..2a915be3 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -158,6 +158,12 @@ class NavigationActivity : AppCompatActivity() { var showWelcomeScreen = Util.isFirstRun() val areServersMigrated: Boolean = serverSettingsModel.migrateFromPreferences() + // Migrate Feature storage if needed + // TODO: Remove in December 2022 + if (!Settings.hasKey(Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING)) { + Settings.migrateFeatureStorage() + } + // If there are any servers in the DB, do not show the welcome screen showWelcomeScreen = showWelcomeScreen and !areServersMigrated diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt index 6c58d85a..2ec56ddb 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/TrackViewHolder.kt @@ -13,12 +13,9 @@ import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.RecyclerView import io.reactivex.rxjava3.disposables.Disposable import org.koin.core.component.KoinComponent -import org.koin.core.component.get import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.domain.MusicDirectory -import org.moire.ultrasonic.featureflags.Feature -import org.moire.ultrasonic.featureflags.FeatureStorage import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.DownloadStatus import org.moire.ultrasonic.service.MusicServiceFactory @@ -61,11 +58,6 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable var observableChecked = MutableLiveData(false) - private val useFiveStarRating: Boolean by lazy { - val features: FeatureStorage = get() - features.isFeatureEnabled(Feature.FIVE_STAR_RATING) - } - lateinit var imageHelper: Utils.ImageHelper fun setSong( @@ -74,6 +66,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable draggable: Boolean, isSelected: Boolean = false ) { + val useFiveStarRating = Settings.useFiveStarRating val song = file.song downloadFile = file entry = song @@ -98,7 +91,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable star.isVisible = false rating.isVisible = false } else { - setupStarButtons(song) + setupStarButtons(song, useFiveStarRating) } updateProgress(downloadFile!!.progress.value!!) @@ -138,7 +131,7 @@ class TrackViewHolder(val view: View) : RecyclerView.ViewHolder(view), Checkable } } - private fun setupStarButtons(song: MusicDirectory.Entry) { + private fun setupStarButtons(song: MusicDirectory.Entry, useFiveStarRating: Boolean) { if (useFiveStarRating) { // Hide single star star.isVisible = false diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt index 36eb3786..2096928f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt @@ -9,7 +9,6 @@ import org.moire.ultrasonic.BuildConfig import org.moire.ultrasonic.di.appPermanentStorage import org.moire.ultrasonic.di.applicationModule import org.moire.ultrasonic.di.baseNetworkModule -import org.moire.ultrasonic.di.featureFlagsModule import org.moire.ultrasonic.di.mediaPlayerModule import org.moire.ultrasonic.di.musicServiceModule import org.moire.ultrasonic.log.FileLoggerTree @@ -47,7 +46,6 @@ class UApp : MultiDexApplication() { applicationModule, appPermanentStorage, baseNetworkModule, - featureFlagsModule, musicServiceModule, mediaPlayerModule ) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/FeatureFlagsModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/FeatureFlagsModule.kt deleted file mode 100644 index 303f058a..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/FeatureFlagsModule.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.moire.ultrasonic.di - -import org.koin.android.ext.koin.androidContext -import org.koin.dsl.module -import org.moire.ultrasonic.featureflags.FeatureStorage - -/** - * This Koin module contains the registration for the Feature Flags - */ -val featureFlagsModule = module { - factory { FeatureStorage(androidContext()) } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/Feature.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/Feature.kt deleted file mode 100644 index a92edbc4..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/Feature.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.moire.ultrasonic.featureflags - -/** - * Contains a list of new features/implementations that are not yet finished, - * but possible to try it out. - */ -enum class Feature( - val defaultValue: Boolean -) { - /** - * Enables new image downloader implementation. - */ - NEW_IMAGE_DOWNLOADER(false), - /** - * Enables five star rating system. - */ - FIVE_STAR_RATING(false) -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/FeatureStorage.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/FeatureStorage.kt deleted file mode 100644 index dca9bb4f..00000000 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/FeatureStorage.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.moire.ultrasonic.featureflags - -import android.content.Context - -private const val SP_NAME = "feature_flags" - -/** - * Provides storage for current feature flag state. - */ -class FeatureStorage( - context: Context -) { - private val sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE) - - /** - * Get [feature] current enabled state. - */ - fun isFeatureEnabled(feature: Feature): Boolean { - return sp.getBoolean(feature.name, feature.defaultValue) - } - - /** - * Update [feature] enabled state to [isEnabled]. - */ - fun changeFeatureFlag( - feature: Feature, - isEnabled: Boolean - ) { - sp.edit().putBoolean(feature.name, isEnabled).apply() - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt index 940fb1b5..6969a47f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/MainFragment.kt @@ -4,16 +4,15 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.AdapterView.OnItemClickListener -import android.widget.ListView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.Navigation import org.koin.core.component.KoinComponent import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.util.Constants -import org.moire.ultrasonic.util.MergeAdapter import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.Util @@ -21,26 +20,26 @@ import org.moire.ultrasonic.util.Util * Displays the Main screen of Ultrasonic, where the music library can be browsed */ class MainFragment : Fragment(), KoinComponent { - private var list: ListView? = null - private lateinit var musicTitle: View - private lateinit var artistsButton: View - private lateinit var albumsButton: View - private lateinit var genresButton: View - private lateinit var videosTitle: View - private lateinit var songsTitle: View - private lateinit var randomSongsButton: View - private lateinit var songsStarredButton: View - private lateinit var albumsTitle: View - private lateinit var albumsNewestButton: View - private lateinit var albumsRandomButton: View - private lateinit var albumsHighestButton: View - private lateinit var albumsStarredButton: View - private lateinit var albumsRecentButton: View - private lateinit var albumsFrequentButton: View - private lateinit var albumsAlphaByNameButton: View - private lateinit var albumsAlphaByArtistButton: View - private lateinit var videosButton: View + private lateinit var list: LinearLayout + private lateinit var musicTitle: TextView + private lateinit var artistsButton: TextView + private lateinit var albumsButton: TextView + private lateinit var genresButton: TextView + private lateinit var videosTitle: TextView + private lateinit var songsTitle: TextView + private lateinit var randomSongsButton: TextView + private lateinit var songsStarredButton: TextView + private lateinit var albumsTitle: TextView + private lateinit var albumsNewestButton: TextView + private lateinit var albumsRandomButton: TextView + private lateinit var albumsHighestButton: TextView + private lateinit var albumsStarredButton: TextView + private lateinit var albumsRecentButton: TextView + private lateinit var albumsFrequentButton: TextView + private lateinit var albumsAlphaByNameButton: TextView + private lateinit var albumsAlphaByArtistButton: TextView + private lateinit var videosButton: TextView override fun onCreate(savedInstanceState: Bundle?) { Util.applyTheme(this.context) @@ -59,8 +58,8 @@ class MainFragment : Fragment(), KoinComponent { list = view.findViewById(R.id.main_list) setupButtons() - - if (list != null) setupMenuList(list!!) + setupClickListener() + setupItemVisibility() super.onViewCreated(view, savedInstanceState) } @@ -71,134 +70,128 @@ class MainFragment : Fragment(), KoinComponent { val currentId3Setting = Settings.shouldUseId3Tags // If setting has changed... - if (currentId3Setting != shouldUseId3) { - shouldUseId3 = currentId3Setting + if (currentId3Setting != cachedId3Setting) { + cachedId3Setting = currentId3Setting shouldRestart = true } // then setup the list anew. if (shouldRestart) { - if (list != null) setupMenuList(list!!) + setupItemVisibility() } } private fun setupButtons() { - val buttons = layoutInflater.inflate(R.layout.main_buttons, list, false) - musicTitle = buttons.findViewById(R.id.main_music) - artistsButton = buttons.findViewById(R.id.main_artists_button) - albumsButton = buttons.findViewById(R.id.main_albums_button) - genresButton = buttons.findViewById(R.id.main_genres_button) - videosTitle = buttons.findViewById(R.id.main_videos_title) - songsTitle = buttons.findViewById(R.id.main_songs) - randomSongsButton = buttons.findViewById(R.id.main_songs_button) - songsStarredButton = buttons.findViewById(R.id.main_songs_starred) - albumsTitle = buttons.findViewById(R.id.main_albums) - albumsNewestButton = buttons.findViewById(R.id.main_albums_newest) - albumsRandomButton = buttons.findViewById(R.id.main_albums_random) - albumsHighestButton = buttons.findViewById(R.id.main_albums_highest) - albumsStarredButton = buttons.findViewById(R.id.main_albums_starred) - albumsRecentButton = buttons.findViewById(R.id.main_albums_recent) - albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent) - albumsAlphaByNameButton = buttons.findViewById(R.id.main_albums_alphaByName) - albumsAlphaByArtistButton = buttons.findViewById(R.id.main_albums_alphaByArtist) - videosButton = buttons.findViewById(R.id.main_videos) + musicTitle = list.findViewById(R.id.main_music) + artistsButton = list.findViewById(R.id.main_artists_button) + albumsButton = list.findViewById(R.id.main_albums_button) + genresButton = list.findViewById(R.id.main_genres_button) + videosTitle = list.findViewById(R.id.main_videos_title) + songsTitle = list.findViewById(R.id.main_songs) + randomSongsButton = list.findViewById(R.id.main_songs_button) + songsStarredButton = list.findViewById(R.id.main_songs_starred) + albumsTitle = list.findViewById(R.id.main_albums) + albumsNewestButton = list.findViewById(R.id.main_albums_newest) + albumsRandomButton = list.findViewById(R.id.main_albums_random) + albumsHighestButton = list.findViewById(R.id.main_albums_highest) + albumsStarredButton = list.findViewById(R.id.main_albums_starred) + albumsRecentButton = list.findViewById(R.id.main_albums_recent) + albumsFrequentButton = list.findViewById(R.id.main_albums_frequent) + albumsAlphaByNameButton = list.findViewById(R.id.main_albums_alphaByName) + albumsAlphaByArtistButton = list.findViewById(R.id.main_albums_alphaByArtist) + videosButton = list.findViewById(R.id.main_videos) } - private fun setupMenuList(list: ListView) { + private fun setupItemVisibility() { + // Cache some values + cachedId3Setting = Settings.shouldUseId3Tags + val isOnline = !isOffline() - // TODO: Should use RecyclerView - val adapter = MergeAdapter() + // Music + musicTitle.isVisible = true + artistsButton.isVisible = true + albumsButton.isVisible = isOnline + genresButton.isVisible = true - shouldUseId3 = Settings.shouldUseId3Tags + // Songs + songsTitle.isVisible = isOnline + randomSongsButton.isVisible = true + songsStarredButton.isVisible = isOnline - if (!isOffline()) { - adapter.addView(musicTitle, false) - adapter.addViews(listOf(artistsButton, albumsButton, genresButton), true) - adapter.addView(songsTitle, false) - adapter.addViews(listOf(randomSongsButton, songsStarredButton), true) - adapter.addView(albumsTitle, false) - adapter.addViews( - listOf( - albumsNewestButton, - albumsRecentButton, - albumsFrequentButton - ), - true - ) - if (!shouldUseId3) { - adapter.addView(albumsHighestButton, true) - } - adapter.addViews( - listOf( - albumsRandomButton, - albumsStarredButton, - albumsAlphaByNameButton, - albumsAlphaByArtistButton - ), - true - ) - adapter.addView(videosTitle, false) - adapter.addViews(listOf(videosButton), true) - } else { - // Offline supported calls - adapter.addView(musicTitle, false) - adapter.addViews(listOf(artistsButton, genresButton), true) - adapter.addView(songsTitle, false) - adapter.addView(randomSongsButton, true) - } + // Albums + albumsTitle.isVisible = isOnline + albumsNewestButton.isVisible = isOnline + albumsRecentButton.isVisible = isOnline + albumsFrequentButton.isVisible = isOnline + albumsHighestButton.isVisible = isOnline && !cachedId3Setting + albumsRandomButton.isVisible = isOnline + albumsStarredButton.isVisible = isOnline + albumsAlphaByNameButton.isVisible = isOnline + albumsAlphaByArtistButton.isVisible = isOnline - list.adapter = adapter - list.onItemClickListener = listListener + // Videos + videosTitle.isVisible = isOnline + videosButton.isVisible = isOnline } - private val listListener = - OnItemClickListener { _: AdapterView<*>?, view: View, _: Int, _: Long -> - when { - view === albumsNewestButton -> { - showAlbumList("newest", R.string.main_albums_newest) - } - view === albumsRandomButton -> { - showAlbumList("random", R.string.main_albums_random) - } - view === albumsHighestButton -> { - showAlbumList("highest", R.string.main_albums_highest) - } - view === albumsRecentButton -> { - showAlbumList("recent", R.string.main_albums_recent) - } - view === albumsFrequentButton -> { - showAlbumList("frequent", R.string.main_albums_frequent) - } - view === albumsStarredButton -> { - showAlbumList(Constants.STARRED, R.string.main_albums_starred) - } - view === albumsAlphaByNameButton -> { - showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_alphaByName) - } - view === albumsAlphaByArtistButton -> { - showAlbumList("alphabeticalByArtist", R.string.main_albums_alphaByArtist) - } - view === songsStarredButton -> { - showStarredSongs() - } - view === artistsButton -> { - showArtists() - } - view === albumsButton -> { - showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_title) - } - view === randomSongsButton -> { - showRandomSongs() - } - view === genresButton -> { - showGenres() - } - view === videosButton -> { - showVideos() - } - } + private fun setupClickListener() { + albumsNewestButton.setOnClickListener { + showAlbumList("newest", R.string.main_albums_newest) } + albumsRandomButton.setOnClickListener { + showAlbumList("random", R.string.main_albums_random) + } + + albumsHighestButton.setOnClickListener { + showAlbumList("highest", R.string.main_albums_highest) + } + + albumsRecentButton.setOnClickListener { + showAlbumList("recent", R.string.main_albums_recent) + } + + albumsFrequentButton.setOnClickListener { + showAlbumList("frequent", R.string.main_albums_frequent) + } + + albumsStarredButton.setOnClickListener { + showAlbumList(Constants.STARRED, R.string.main_albums_starred) + } + + albumsAlphaByNameButton.setOnClickListener { + showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_alphaByName) + } + + albumsAlphaByArtistButton.setOnClickListener { + showAlbumList("alphabeticalByArtist", R.string.main_albums_alphaByArtist) + } + + songsStarredButton.setOnClickListener { + showStarredSongs() + } + + artistsButton.setOnClickListener { + showArtists() + } + + albumsButton.setOnClickListener { + showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_title) + } + + randomSongsButton.setOnClickListener { + showRandomSongs() + } + + genresButton.setOnClickListener { + showGenres() + } + + videosButton.setOnClickListener { + showVideos() + } + } + private fun showStarredSongs() { val bundle = Bundle() bundle.putInt(Constants.INTENT_STARRED, 1) @@ -242,6 +235,6 @@ class MainFragment : Fragment(), KoinComponent { } companion object { - private var shouldUseId3 = false + private var cachedId3Setting = false } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index 04f9c2d3..e1a145ae 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -70,8 +70,6 @@ import org.moire.ultrasonic.domain.Identifiable import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.RepeatMode -import org.moire.ultrasonic.featureflags.Feature -import org.moire.ultrasonic.featureflags.FeatureStorage import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.service.DownloadFile import org.moire.ultrasonic.service.LocalMediaPlayer @@ -210,7 +208,7 @@ class PlayerFragment : val width = size.x val height = size.y setHasOptionsMenu(true) - useFiveStarRating = get().isFeatureEnabled(Feature.FIVE_STAR_RATING) + useFiveStarRating = Settings.useFiveStarRating swipeDistance = (width + height) * PERCENTAGE_OF_SCREEN_FOR_SWIPE / 100 swipeVelocity = swipeDistance gestureScanner = GestureDetector(context, this) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt index e49a79fd..b551fd29 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt @@ -23,12 +23,9 @@ import androidx.preference.PreferenceFragmentCompat import java.io.File import kotlin.math.ceil import org.koin.core.component.KoinComponent -import org.koin.java.KoinJavaComponent.get import org.koin.java.KoinJavaComponent.inject import org.moire.ultrasonic.R import org.moire.ultrasonic.app.UApp -import org.moire.ultrasonic.featureflags.Feature -import org.moire.ultrasonic.featureflags.FeatureStorage import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.log.FileLoggerTree import org.moire.ultrasonic.log.FileLoggerTree.Companion.deleteLogFiles @@ -144,7 +141,6 @@ class SettingsFragment : sharingDefaultGreeting!!.text = shareGreeting setupClearSearchPreference() - setupFeatureFlagsPreferences() setupCacheLocationPreference() setupBluetoothDevicePreferences() @@ -377,21 +373,6 @@ class SettingsFragment : } } - private fun setupFeatureFlagsPreferences() { - val featureStorage = get(FeatureStorage::class.java) - val useFiveStarRating = findPreference( - Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING - ) as CheckBoxPreference? - if (useFiveStarRating != null) { - useFiveStarRating.isChecked = featureStorage.isFeatureEnabled(Feature.FIVE_STAR_RATING) - useFiveStarRating.onPreferenceChangeListener = - Preference.OnPreferenceChangeListener { _: Preference?, o: Any? -> - featureStorage.changeFeatureFlag(Feature.FIVE_STAR_RATING, (o as Boolean?)!!) - true - } - } - } - private fun update() { theme!!.summary = theme!!.entry maxBitrateWifi!!.summary = maxBitrateWifi!!.entry diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt index 55bffc4c..55cf2796 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerController.kt @@ -8,15 +8,12 @@ package org.moire.ultrasonic.service import android.content.Intent import org.koin.core.component.KoinComponent -import org.koin.core.component.get import org.koin.core.component.inject import org.moire.ultrasonic.app.UApp import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.domain.PlayerState import org.moire.ultrasonic.domain.RepeatMode -import org.moire.ultrasonic.featureflags.Feature -import org.moire.ultrasonic.featureflags.FeatureStorage import org.moire.ultrasonic.service.MediaPlayerService.Companion.executeOnStartedMediaPlayerService import org.moire.ultrasonic.service.MediaPlayerService.Companion.getInstance import org.moire.ultrasonic.service.MediaPlayerService.Companion.runningInstance @@ -473,8 +470,7 @@ class MediaPlayerController( @Suppress("TooGenericExceptionCaught") // The interface throws only generic exceptions fun setSongRating(rating: Int) { - val features: FeatureStorage = get() - if (!features.isFeatureEnabled(Feature.FIVE_STAR_RATING)) return + if (!Settings.useFiveStarRating) return if (localMediaPlayer.currentPlaying == null) return val song = localMediaPlayer.currentPlaying!!.song song.userRating = rating diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt index 29c13b18..e5240e33 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt @@ -297,11 +297,6 @@ object Settings { return 0 } - var shouldShowAllSongsByArtist by BooleanSetting( - Constants.PREFERENCES_KEY_SHOW_ALL_SONGS_BY_ARTIST, - false - ) - @JvmStatic var resumeOnBluetoothDevice by IntSetting( Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE, @@ -320,6 +315,18 @@ object Settings { val preferences: SharedPreferences get() = PreferenceManager.getDefaultSharedPreferences(Util.appContext()) + var useFiveStarRating by BooleanSetting(Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING, false) + + // TODO: Remove in December 2022 + fun migrateFeatureStorage() { + val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE) + useFiveStarRating = sp.getBoolean("FIVE_STAR_RATING", false) + } + + fun hasKey(key: String): Boolean { + return preferences.contains(key) + } + private val appContext: Context get() { return UApp.applicationContext() diff --git a/ultrasonic/src/main/res/layout/main.xml b/ultrasonic/src/main/res/layout/main.xml index dcd8fbd0..e6763155 100644 --- a/ultrasonic/src/main/res/layout/main.xml +++ b/ultrasonic/src/main/res/layout/main.xml @@ -1,18 +1,211 @@ + a:orientation="vertical"> - + a:layout_height="wrap_content" + a:gravity="center_vertical" + a:paddingStart="6dp" + a:text="@string/main.music" + a:textAllCaps="true" + a:textAppearance="?android:attr/textAppearanceSmall" + a:textColor="@color/cyan" + a:textStyle="bold" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/ultrasonic/src/main/res/layout/main_buttons.xml b/ultrasonic/src/main/res/layout/main_buttons.xml deleted file mode 100644 index ef1395c7..00000000 --- a/ultrasonic/src/main/res/layout/main_buttons.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ultrasonic/src/main/res/values-cs/strings.xml b/ultrasonic/src/main/res/values-cs/strings.xml index d4819465..034a12f0 100644 --- a/ultrasonic/src/main/res/values-cs/strings.xml +++ b/ultrasonic/src/main/res/values-cs/strings.xml @@ -428,10 +428,10 @@ Nekompatibilní verze. Aktualizujte prosím Ultrasonic Android aplikaci. Nekompatibilní verze. Aktualizujte prosím Subsonic server. - - Příznaky funkcí - Používat pět hvězdiček pro hodnocení skladeb - Používat pět hvězdiček pro hodnocení skladeb + + Příznaky funkcí + Používat pět hvězdiček pro hodnocení skladeb + Používat pět hvězdiček pro hodnocení skladeb namísto jednoduchého jednohvězdičkového hodnocení. diff --git a/ultrasonic/src/main/res/values-de/strings.xml b/ultrasonic/src/main/res/values-de/strings.xml index 03b7b4a1..32fedc2a 100644 --- a/ultrasonic/src/main/res/values-de/strings.xml +++ b/ultrasonic/src/main/res/values-de/strings.xml @@ -360,10 +360,10 @@ Inkompatible Versionen. Bitte die Ultrasonic App aktualisieren. Inkompatible Versionen. Bitte den subsonic Server aktualisieren. - - Funktionseinstellungem - Verwenden Sie Fünf-Sterne-Bewertung für Songs - Verwenden Sie Fünf-Sterne-Bewertungssystem für Songs + + Funktionseinstellungem + Verwenden Sie Fünf-Sterne-Bewertung für Songs + Verwenden Sie Fünf-Sterne-Bewertungssystem für Songs          anstatt einfach Elemente zu markieren / zu entfernen. diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 75ef533d..c3010b41 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -450,10 +450,10 @@ Versiones incompatibles. Por favor actualiza la aplicación de Android Ultrasonic. Versiones incompatibles. Por favor actualiza el servidor de Subsonic. - - Funciones experimentales - Use cinco estrellas para las canciones - Utilice el sistema de calificación de cinco estrellas para canciones + + Funciones experimentales + Use cinco estrellas para las canciones + Utilice el sistema de calificación de cinco estrellas para canciones en lugar de simplemente destacar / desestimar elementos. diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index d59744f2..c538e37a 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -439,10 +439,10 @@ Versions incompatibles. Veuillez mette à jour l\'application Android Ultrasonic. Versions incompatibles. Veuillez mette à jour le serveur Subsonic. - - Drapeaux des fonctionnalités - Utiliser les étoiles pour noter les morceaux - Utiliser un système de notation à base d\'étoiles pour les morceaux + + Drapeaux des fonctionnalités + Utiliser les étoiles pour noter les morceaux + Utiliser un système de notation à base d\'étoiles pour les morceaux au lieu de simplement mettre en avant les morceaux. diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index d8f35055..efa3dcd0 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -434,10 +434,10 @@ Nem kompatibilis verzió. Kérjük, frissítse az Ultrasonic Android alkalmazást! Nem kompatibilis verzió. Kérjük, frissítse a Subsonic kiszolgálót! - - Jellemzők Zászlók - Öt csillagos értékelés használata a dalokhoz - Öt csillag használata az értékeléshez az egyszerű + + Jellemzők Zászlók + Öt csillagos értékelés használata a dalokhoz + Öt csillag használata az értékeléshez az egyszerű csillaggal jelölés helyett. diff --git a/ultrasonic/src/main/res/values-nl/strings.xml b/ultrasonic/src/main/res/values-nl/strings.xml index 5478b41b..194afb24 100644 --- a/ultrasonic/src/main/res/values-nl/strings.xml +++ b/ultrasonic/src/main/res/values-nl/strings.xml @@ -450,10 +450,10 @@ Incompatibele versies. Werk de Ultrasonic Android-app bij. Incompatibele versies. Werk je Subsonic-server bij. - - Experimentele functies - Gebruik vijf sterren voor nummers - Gebruik vijf sterren ratingsysteem voor liedjes + + Experimentele functies + Gebruik vijf sterren voor nummers + Gebruik vijf sterren ratingsysteem voor liedjes in plaats van items simpelweg in de hoofdrol te zetten / niet te verwijderen. diff --git a/ultrasonic/src/main/res/values-pl/strings.xml b/ultrasonic/src/main/res/values-pl/strings.xml index 1651e37e..8832e780 100644 --- a/ultrasonic/src/main/res/values-pl/strings.xml +++ b/ultrasonic/src/main/res/values-pl/strings.xml @@ -404,10 +404,10 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników Brak zgodności wersji. Uaktualnij aplikację Ultrasonic na Androida. Brak zgodności wersji. Uaktualnij serwer Subsonic. - - Flagi funkcji - Użyj pięciu gwiazdek dla utworów - W przypadku utworów użyj systemu pięciu gwiazdek + + Flagi funkcji + Użyj pięciu gwiazdek dla utworów + W przypadku utworów użyj systemu pięciu gwiazdek zamiast po prostu grać gwiazdkami / bez gwiazd. diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index 781e6ec3..5fd380a2 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -443,10 +443,10 @@ Versões incompativeis. Atualize o aplicativo Ultrasonic para Android. Versões incompativeis. Atualize o servidor Ultrasonic. - - Sinalização de Recursos - Usar Classif. 5 Estrelas para Músicas - Usar o sistema de classificação de 5 estrelas para músicas + + Sinalização de Recursos + Usar Classif. 5 Estrelas para Músicas + Usar o sistema de classificação de 5 estrelas para músicas em vez de simplesmente estrelar/não estrelar itens diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 21d56dae..1a277b2d 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -389,10 +389,10 @@ Versões incompativeis. Atualize o aplicativo Ultrasonic para Android. Versões incompativeis. Atualize o servidor Ultrasonic. - - Bandeiras de recursos - Use classificação de cinco estrelas para músicas - Use o sistema de classificação de cinco estrelas para músicas + + Bandeiras de recursos + Use classificação de cinco estrelas para músicas + Use o sistema de classificação de cinco estrelas para músicas em vez de simplesmente estrelar / não estrelar itens. diff --git a/ultrasonic/src/main/res/values-ru/strings.xml b/ultrasonic/src/main/res/values-ru/strings.xml index 9be7d70b..28c81902 100644 --- a/ultrasonic/src/main/res/values-ru/strings.xml +++ b/ultrasonic/src/main/res/values-ru/strings.xml @@ -453,10 +453,10 @@ Несовместимые версии. Пожалуйста, обновите приложение Ultrasonic для Android. Несовместимые версии. Пожалуйста, обновите Subsonic сервер. - - Флаги - Использовать пятизвездочный рейтинг для песен - Использовать пятизвездочную систему рейтинга для песен + + Флаги + Использовать пятизвездочный рейтинг для песен + Использовать пятизвездочную систему рейтинга для песен вместо того, чтобы просто ставить/не ставить звезды. diff --git a/ultrasonic/src/main/res/values-zh-rCN/strings.xml b/ultrasonic/src/main/res/values-zh-rCN/strings.xml index 7e4a1e04..97927f41 100644 --- a/ultrasonic/src/main/res/values-zh-rCN/strings.xml +++ b/ultrasonic/src/main/res/values-zh-rCN/strings.xml @@ -432,10 +432,10 @@ 版本不兼容,请升级 Ultrasonic 应用。 不兼容的版本。请升级Subsonic 服务。 - - 特性标志 - 为歌曲使用五星评分 - 对歌曲使用五星级评级系统 + + 特性标志 + 为歌曲使用五星评分 + 对歌曲使用五星级评级系统 而不是简单地为项目加星标/取消星标。 diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index 1e744543..04bbc31f 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -461,10 +461,10 @@ Incompatible versions. Please upgrade Ultrasonic Android app. Incompatible versions. Please upgrade Subsonic server. - - Feature Flags - Use five star rating for songs - Use five star rating system for songs + + Features + Use five star rating for songs + Use five star rating system for songs instead of simply starring/unstarring items. diff --git a/ultrasonic/src/main/res/xml/settings.xml b/ultrasonic/src/main/res/xml/settings.xml index 73c611f6..5f96a81b 100644 --- a/ultrasonic/src/main/res/xml/settings.xml +++ b/ultrasonic/src/main/res/xml/settings.xml @@ -345,14 +345,14 @@ app:iconSpaceReserved="false"/> + a:summary="@string/settings.five_star_rating_description" + a:title="@string/settings.five_star_rating_title" + app:iconSpaceReserved="false" />