From 5412f59f6193411b3c2f08b2bbfccf3f09310c4f Mon Sep 17 00:00:00 2001 From: Michatec <121869403+Michatec@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:25:37 +0100 Subject: [PATCH] - New Version Initialized - Some deprecated fixes - New functions and fallbacks - Some bug fixes --- app/build.gradle | 25 ++-- .../java/com/michatec/radio/PlayerFragment.kt | 42 ++----- .../java/com/michatec/radio/PlayerService.kt | 112 ++++-------------- .../com/michatec/radio/SettingsFragment.kt | 2 +- .../radio/collection/CollectionAdapter.kt | 20 ++-- .../radio/search/SearchResultAdapter.kt | 6 +- app/src/main/res/values/strings.xml | 2 +- gradle.properties | 1 - 8 files changed, 65 insertions(+), 145 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0557806..519bf26 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,20 +1,27 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-parcelize' +plugins { + id 'com.android.application' + id 'kotlin-parcelize' +} + +androidComponents { + onVariants(selector().all()) { variant -> + variant.outputs.forEach { output -> + output.outputFileName.set("Radio.apk") + } + } +} android { namespace 'com.michatec.radio' - compileSdk 35 + compileSdk 36 defaultConfig { applicationId 'com.michatec.radio' minSdk 28 - //noinspection OldTargetApi - targetSdk 35 - versionCode 130 - versionName '13' + targetSdk 36 + versionCode 140 + versionName '14' resourceConfigurations += ['en', 'de', 'el', 'nl', 'pl', 'ru','uk', 'ja', 'da', 'fr'] - setProperty('archivesBaseName', 'Radio') } compileOptions { diff --git a/app/src/main/java/com/michatec/radio/PlayerFragment.kt b/app/src/main/java/com/michatec/radio/PlayerFragment.kt index 343bd21..68a274d 100644 --- a/app/src/main/java/com/michatec/radio/PlayerFragment.kt +++ b/app/src/main/java/com/michatec/radio/PlayerFragment.kt @@ -182,7 +182,7 @@ class PlayerFragment : Fragment(), (activity as AppCompatActivity).supportActionBar?.hide() // set the same background color of the player sheet for the navigation bar - (activity as AppCompatActivity).window.navigationBarColor = ContextCompat.getColor(requireActivity(), R.color.player_sheet_background) + requireActivity().window.navigationBarColor = ContextCompat.getColor(requireActivity(), R.color.player_sheet_background) // associate the ItemTouchHelper with the RecyclerView itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback()) @@ -211,14 +211,14 @@ class PlayerFragment : Fragment(), } override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { - val fromPosition = viewHolder.adapterPosition - val toPosition = target.adapterPosition + val fromPosition = viewHolder.bindingAdapterPosition + val toPosition = target.bindingAdapterPosition collectionAdapter.onItemMove(fromPosition, toPosition) return true } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - val position = viewHolder.adapterPosition + val position = viewHolder.bindingAdapterPosition collectionAdapter.onItemDismiss(position) } @@ -435,15 +435,15 @@ class PlayerFragment : Fragment(), val swipeToDeleteHandler = object : UiHelper.SwipeToDeleteCallback(activity as Context) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { // ask user - val adapterPosition: Int = viewHolder.adapterPosition + val bindingAdapterPosition: Int = viewHolder.bindingAdapterPosition val dialogMessage = - "${getString(R.string.dialog_yes_no_message_remove_station)}\n\n- ${collection.stations[adapterPosition].name}" + "${getString(R.string.dialog_yes_no_message_remove_station)}\n\n- ${collection.stations[bindingAdapterPosition].name}" YesNoDialog(this@PlayerFragment as YesNoDialog.YesNoDialogListener).show( context = activity as Context, type = Keys.DIALOG_REMOVE_STATION, messageString = dialogMessage, yesButton = R.string.dialog_yes_no_positive_button_remove_station, - payload = adapterPosition + payload = bindingAdapterPosition ) } } @@ -455,8 +455,8 @@ class PlayerFragment : Fragment(), object : UiHelper.SwipeToMarkStarredCallback(activity as Context) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { // mark card starred - val adapterPosition: Int = viewHolder.adapterPosition - collectionAdapter.toggleStarredStation(activity as Context, adapterPosition) + val bindingAdapterPosition: Int = viewHolder.bindingAdapterPosition + collectionAdapter.toggleStarredStation(activity as Context, bindingAdapterPosition) } } val swipeToMarkStarredItemTouchHelper = ItemTouchHelper(swipeToMarkStarredHandler) @@ -501,22 +501,6 @@ class PlayerFragment : Fragment(), } - -// /* Sets up the general playback controls - Note: station specific controls and views are updated in updatePlayerViews() */ -// // it is probably okay to suppress this warning - the OnTouchListener on the time played view does only toggle the time duration / remaining display -// private fun setupPlaybackControls() { -// -// // main play/pause button -// layout.playButtonView.setOnClickListener { -// onPlayButtonTapped(playerState.stationUuid, playerState.playbackState) -// //onPlayButtonTapped(playerState.stationUuid, playerController.getPlaybackState().state) // todo remove -// } -// -// // register a callback to stay in sync -// playerController.registerCallback(mediaControllerCallback) -// } - - /* Sets up the player */ private fun updatePlayerViews() { // get station @@ -591,7 +575,7 @@ class PlayerFragment : Fragment(), /* Handles ACTION_SHOW_PLAYER request from notification */ private fun handleShowPlayer() { Log.i(TAG, "Tap on notification registered.") - // todo implement + layout.showPlayer(requireActivity()) } @@ -804,7 +788,7 @@ class PlayerFragment : Fragment(), .setAction(R.string.snackbar_show) { val releaseurl = getString(R.string.snackbar_url_app_home_page) val i = Intent(Intent.ACTION_VIEW) - i.data = Uri.parse(releaseurl) + i.data = releaseurl.toUri() // Not sure that does anything i.putExtra("SOURCE", "SELF") startActivity(i) @@ -823,9 +807,5 @@ class PlayerFragment : Fragment(), request.tag = TAG queue.add(request) } - - /* - * End of declaration - */ } diff --git a/app/src/main/java/com/michatec/radio/PlayerService.kt b/app/src/main/java/com/michatec/radio/PlayerService.kt index da1a2df..f5aa621 100644 --- a/app/src/main/java/com/michatec/radio/PlayerService.kt +++ b/app/src/main/java/com/michatec/radio/PlayerService.kt @@ -397,75 +397,6 @@ class PlayerService : MediaLibraryService() { return super.onCustomCommand(session, controller, customCommand, args) } - override fun onPlayerCommandRequest( - session: MediaSession, - controller: MediaSession.ControllerInfo, - playerCommand: Int - ): Int { - // playerCommand = one of COMMAND_PLAY_PAUSE, COMMAND_PREPARE, COMMAND_STOP, COMMAND_SEEK_TO_DEFAULT_POSITION, COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_MEDIA_ITEM, COMMAND_SEEK_BACK, COMMAND_SEEK_FORWARD, COMMAND_SET_SPEED_AND_PITCH, COMMAND_SET_SHUFFLE_MODE, COMMAND_SET_REPEAT_MODE, COMMAND_GET_CURRENT_MEDIA_ITEM, COMMAND_GET_TIMELINE, COMMAND_GET_MEDIA_ITEMS_METADATA, COMMAND_SET_MEDIA_ITEMS_METADATA, COMMAND_CHANGE_MEDIA_ITEMS, COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, COMMAND_SET_DEVICE_VOLUME, COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_SET_VIDEO_SURFACE, COMMAND_GET_TEXT, COMMAND_SET_TRACK_SELECTION_PARAMETERS or COMMAND_GET_TRACK_INFOS. */ - // emulate headphone buttons - // start/pause: adb shell input keyevent 85 - // next: adb shell input keyevent 87 - // prev: adb shell input keyevent 88 - when (playerCommand) { - Player.COMMAND_SEEK_TO_NEXT -> { - player.addMediaItem( - CollectionHelper.getNextMediaItem( - this@PlayerService, - collection, - player.currentMediaItem?.mediaId ?: String() - ) - ) - player.prepare() - player.play() - return SessionResult.RESULT_SUCCESS - } - Player.COMMAND_SEEK_TO_PREVIOUS -> { - player.addMediaItem( - CollectionHelper.getPreviousMediaItem( - this@PlayerService, - collection, - player.currentMediaItem?.mediaId ?: String() - ) - ) - player.prepare() - player.play() - return SessionResult.RESULT_SUCCESS - } - Player.COMMAND_PREPARE -> { - return if (playLastStation) { - // special case: system requested media resumption (see also onGetLibraryRoot) - player.addMediaItem(CollectionHelper.getRecent(this@PlayerService, collection)) - player.prepare() - playLastStation = false - SessionResult.RESULT_SUCCESS - } else { - super.onPlayerCommandRequest(session, controller, playerCommand) - } - } - Player.COMMAND_PLAY_PAUSE -> { - return if (player.isPlaying) { - super.onPlayerCommandRequest(session, controller, playerCommand) - } else { - // seek to the start of the "live window" - player.seekTo(0) - SessionResult.RESULT_SUCCESS - } - } -// Player.COMMAND_PLAY_PAUSE -> { -// // override pause with stop, to prevent unnecessary buffering -// if (player.isPlaying) { -// player.stop() -// return SessionResult.RESULT_INFO_SKIPPED -// } else { -// return super.onPlayerCommandRequest(session, controller, playerCommand) -// } -// } - else -> { - return super.onPlayerCommandRequest(session, controller, playerCommand) - } - } - } } @@ -480,21 +411,21 @@ class PlayerService : MediaLibraryService() { customLayout: ImmutableList, showPauseButton: Boolean ): ImmutableList { - val seekToPreviousCommandButton = CommandButton.Builder().apply { - setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS) - setIconResId(R.drawable.ic_notification_skip_to_previous_36dp) - setEnabled(true) - }.build() - val playCommandButton = CommandButton.Builder().apply { - setPlayerCommand(Player.COMMAND_PLAY_PAUSE) - setIconResId(if (player.isPlaying) R.drawable.ic_notification_stop_36dp else R.drawable.ic_notification_play_36dp) - setEnabled(true) - }.build() - val seekToNextCommandButton = CommandButton.Builder().apply { - setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT) - setIconResId(R.drawable.ic_notification_skip_to_next_36dp) - setEnabled(true) - }.build() + val seekToPreviousCommandButton = CommandButton.Builder() + .setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS) + .setIconResId(R.drawable.ic_notification_skip_to_previous_36dp) + .setEnabled(true) + .build() + val playCommandButton = CommandButton.Builder() + .setPlayerCommand(Player.COMMAND_PLAY_PAUSE) + .setIconResId(if (player.isPlaying) R.drawable.ic_notification_stop_36dp else R.drawable.ic_notification_play_36dp) + .setEnabled(true) + .build() + val seekToNextCommandButton = CommandButton.Builder() + .setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT) + .setIconResId(R.drawable.ic_notification_skip_to_next_36dp) + .setEnabled(true) + .build() val commandButtons: MutableList = mutableListOf( seekToPreviousCommandButton, playCommandButton, @@ -544,19 +475,19 @@ class PlayerService : MediaLibraryService() { when (player.playbackState) { // player is able to immediately play from its current position Player.STATE_READY -> { - // todo + stopSelf() } // buffering - data needs to be loaded Player.STATE_BUFFERING -> { - // todo + stopSelf() } // player finished playing all media Player.STATE_ENDED -> { - // todo + stopSelf() } // initial state or player is stopped or playback failed Player.STATE_IDLE -> { - // todo + stopSelf() } } } @@ -585,7 +516,10 @@ class PlayerService : MediaLibraryService() { override fun onPlayerError(error: PlaybackException) { super.onPlayerError(error) Log.d(TAG, "PlayerError occurred: ${error.errorCodeName}") - // todo: test if playback needs to be restarted + if (error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED || error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT) { + player.prepare() + player.play() + } } diff --git a/app/src/main/java/com/michatec/radio/SettingsFragment.kt b/app/src/main/java/com/michatec/radio/SettingsFragment.kt index d97a63b..4d51518 100644 --- a/app/src/main/java/com/michatec/radio/SettingsFragment.kt +++ b/app/src/main/java/com/michatec/radio/SettingsFragment.kt @@ -62,7 +62,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList (activity as AppCompatActivity).supportActionBar?.show() (activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true) (activity as AppCompatActivity).supportActionBar?.title = getString(R.string.fragment_settings_title) - (activity as AppCompatActivity).window.navigationBarColor = getColor(requireContext(), android.R.attr.colorBackground) + requireActivity().window.navigationBarColor = getColor(requireContext(), android.R.attr.colorBackground) } /* Overrides onCreatePreferences from PreferenceFragmentCompat */ diff --git a/app/src/main/java/com/michatec/radio/collection/CollectionAdapter.kt b/app/src/main/java/com/michatec/radio/collection/CollectionAdapter.kt index 9996dfa..442087e 100644 --- a/app/src/main/java/com/michatec/radio/collection/CollectionAdapter.kt +++ b/app/src/main/java/com/michatec/radio/collection/CollectionAdapter.kt @@ -264,12 +264,12 @@ class CollectionAdapter( override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) stationViewHolder.cancelButton.setOnClickListener { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) UiHelper.hideSoftKeyboard(context, stationViewHolder.stationNameEditView) } stationViewHolder.saveButton.setOnClickListener { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) saveStation( station, @@ -280,15 +280,15 @@ class CollectionAdapter( UiHelper.hideSoftKeyboard(context, stationViewHolder.stationNameEditView) } stationViewHolder.placeOnHomeScreenButton.setOnClickListener { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition ShortcutHelper.placeShortcut(context, station) toggleEditViews(position, station.uuid) UiHelper.hideSoftKeyboard(context, stationViewHolder.stationNameEditView) } stationViewHolder.stationImageChangeView.setOnClickListener { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition collectionAdapterListener.onChangeImageButtonTapped(station.uuid) - stationViewHolder.adapterPosition + stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) UiHelper.hideSoftKeyboard(context, stationViewHolder.stationNameEditView) } @@ -378,7 +378,7 @@ class CollectionAdapter( } stationViewHolder.playButtonView.setOnLongClickListener { if (editStationsEnabled) { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) return@setOnLongClickListener true } else { @@ -387,7 +387,7 @@ class CollectionAdapter( } stationViewHolder.stationNameView.setOnLongClickListener { if (editStationsEnabled) { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) return@setOnLongClickListener true } else { @@ -396,7 +396,7 @@ class CollectionAdapter( } stationViewHolder.stationStarredView.setOnLongClickListener { if (editStationsEnabled) { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) return@setOnLongClickListener true } else { @@ -405,7 +405,7 @@ class CollectionAdapter( } stationViewHolder.stationImageView.setOnLongClickListener { if (editStationsEnabled) { - val position: Int = stationViewHolder.adapterPosition + val position: Int = stationViewHolder.bindingAdapterPosition toggleEditViews(position, station.uuid) return@setOnLongClickListener true } else { @@ -471,7 +471,7 @@ class CollectionAdapter( } else if (holder is StationViewHolder) { // get station from position - collection.stations[holder.getAdapterPosition()] + collection.stations[holder.bindingAdapterPosition] for (data in payloads) { when (data as Int) { diff --git a/app/src/main/java/com/michatec/radio/search/SearchResultAdapter.kt b/app/src/main/java/com/michatec/radio/search/SearchResultAdapter.kt index 91eed94..58ff9cc 100644 --- a/app/src/main/java/com/michatec/radio/search/SearchResultAdapter.kt +++ b/app/src/main/java/com/michatec/radio/search/SearchResultAdapter.kt @@ -106,7 +106,7 @@ class SearchResultAdapter( } // mark selected if necessary - val isSelected = selectedPosition == holder.adapterPosition + val isSelected = selectedPosition == holder.bindingAdapterPosition searchResultViewHolder.searchResultLayout.isSelected = isSelected // toggle text scrolling (marquee) if necessary @@ -121,7 +121,7 @@ class SearchResultAdapter( searchResultViewHolder.searchResultLayout.setOnClickListener { // move marked position val previousSelectedPosition = selectedPosition - selectedPosition = holder.adapterPosition + selectedPosition = holder.bindingAdapterPosition notifyItemChanged(previousSelectedPosition) notifyItemChanged(selectedPosition) @@ -133,7 +133,7 @@ class SearchResultAdapter( resetSelection(false) } else { // get the selected station from searchResults - val selectedStation = searchResults[holder.adapterPosition] + val selectedStation = searchResults[holder.bindingAdapterPosition] // perform pre-playback here performPrePlayback(searchResultViewHolder.searchResultLayout.context, selectedStation.getStreamUri()) // hand over station diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8a5706..17f430d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ - \"Blue\" + \"Red\" App icon depicting an old radio diff --git a/gradle.properties b/gradle.properties index b5a05b5..d108322 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,3 @@ org.gradle.jvmargs=-Xmx1536m # enables androidx repo android.useAndroidX=true -android.enableJetifier=true