mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-14 00:17:15 +03:00
Merge branch '461' into 'master'
4.6.1 Release canditate See merge request ultrasonic/ultrasonic!1065
This commit is contained in:
commit
fe555c076d
@ -87,26 +87,26 @@ Assemble Release:
|
||||
# We generate a signed package for each commit to develop as well as when making a release.
|
||||
# Since the develop signed apk are not persistent they can be downloaded for around 3 weeks before Gitlab deletes them.
|
||||
Generate Signed APK:
|
||||
variables:
|
||||
APK_NAME: ultrasonic-${CI_COMMIT_SHA}
|
||||
stage: Sign APK
|
||||
# We don't need the gradle cache here
|
||||
cache: []
|
||||
script:
|
||||
- openssl aes-256-cbc -K ${ULTRASONIC_KEYSTORE_KEY} -iv ${ULTRASONIC_KEYSTORE_IV} -in ultrasonic-keystore.enc -out ultrasonic-keystore -d
|
||||
- mkdir -p ${CI_PROJECT_DIR}/ultrasonic-release
|
||||
- ${ANDROID_HOME}/build-tools/*/zipalign -v 4 ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk ${CI_PROJECT_DIR}/ultrasonic-release/${APK_NAME}.apk
|
||||
- ${ANDROID_HOME}/build-tools/*/apksigner sign --verbose --ks ${CI_PROJECT_DIR}/ultrasonic-keystore --ks-pass pass:${ULTRASONIC_KEYSTORE_STOREPASS} --key-pass pass:${ULTRASONIC_KEYSTORE_KEYPASS} ${CI_PROJECT_DIR}/ultrasonic-release/${APK_NAME}.apk
|
||||
- ${ANDROID_HOME}/build-tools/*/apksigner verify --verbose ${CI_PROJECT_DIR}/ultrasonic-release/${APK_NAME}.apk
|
||||
- ${ANDROID_HOME}/build-tools/*/zipalign -v 4 ultrasonic/build/outputs/apk/release/ultrasonic-release-unsigned.apk ${CI_PROJECT_DIR}/ultrasonic-release/${PACKAGE_APK}
|
||||
- ${ANDROID_HOME}/build-tools/*/apksigner sign --verbose --ks ${CI_PROJECT_DIR}/ultrasonic-keystore --ks-pass pass:${ULTRASONIC_KEYSTORE_STOREPASS} --key-pass pass:${ULTRASONIC_KEYSTORE_KEYPASS} ${CI_PROJECT_DIR}/ultrasonic-release/${PACKAGE_APK}
|
||||
- ${ANDROID_HOME}/build-tools/*/apksigner verify --verbose ${CI_PROJECT_DIR}/ultrasonic-release/${PACKAGE_APK}
|
||||
artifacts:
|
||||
name: $APK_NAME
|
||||
name: $PACKAGE_APK
|
||||
paths:
|
||||
- ultrasonic-release/
|
||||
rules:
|
||||
- if: $CI_COMMIT_REF_NAME == "develop" && $CI_PROJECT_ID == $ROOT_PROJECT_ID && $CI_PIPELINE_SOURCE != "merge_request_event"
|
||||
# Run when releasing a new tag
|
||||
- if: $CI_COMMIT_TAG && $CI_PROJECT_ID == $ROOT_PROJECT_ID
|
||||
# Or when adding a new commit to develop (but never inside merge events)
|
||||
- if: $CI_COMMIT_REF_NAME == "develop" && $CI_PROJECT_ID == $ROOT_PROJECT_ID && $CI_PIPELINE_SOURCE != "merge_request_event"
|
||||
variables:
|
||||
APK_NAME: ultrasonic-${CI_COMMIT_TAG}
|
||||
PACKAGE_APK: ultrasonic-${CI_COMMIT_SHA}.apk
|
||||
|
||||
|
||||
Publish Signed APK:
|
||||
@ -133,10 +133,19 @@ Release:
|
||||
RoboTest:
|
||||
stage: Release
|
||||
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:latest
|
||||
# We don't need the gradle cache here
|
||||
cache: []
|
||||
script:
|
||||
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
|
||||
- gcloud auth activate-service-account --key-file .secure_files/ultrasonic-61089-8ab2ad46c8a8.json
|
||||
- gcloud firebase test android run --token $FIREBASE_TOKEN --type robo --app ultrasonic-release/${PACKAGE_APK} --device model=Nexus6,version=21,locale=en,orientation=portrait --device model=Nexus7,version=19,locale=fr,orientation=landscape
|
||||
- gcloud auth activate-service-account --key-file .secure_files/firebase-key.json
|
||||
- gcloud firebase test android run --project ultrasonic-61089 --type robo --app ultrasonic-release/${PACKAGE_APK} --robo-directives click:button1= --device model=Nexus6,version=21,locale=en,orientation=portrait --device model=Pixel3,version=28,locale=fr,orientation=landscape --device model=Pixel5,version=30,locale=zh,orientation=portrait
|
||||
rules:
|
||||
# Run when releasing a new tag
|
||||
- if: $CI_COMMIT_TAG && $CI_PROJECT_ID == $ROOT_PROJECT_ID
|
||||
# or when requested by using [ROBO] inside the commit message and merging to develop
|
||||
# Would be nice to be able to run it in a MR as well, but currently not possible
|
||||
# Because it would not have access to the protected keys.
|
||||
- if: $CI_COMMIT_MESSAGE =~ /^\[ROBO\].*/ && $CI_PROJECT_ID == $ROOT_PROJECT_ID && $CI_COMMIT_REF_NAME == "develop" && $CI_PIPELINE_SOURCE != "merge_request_event"
|
||||
variables:
|
||||
PACKAGE_APK: ultrasonic-${CI_COMMIT_SHA}.apk
|
||||
|
||||
|
15
fastlane/metadata/android/en-US/changelogs/124.txt
Normal file
15
fastlane/metadata/android/en-US/changelogs/124.txt
Normal file
@ -0,0 +1,15 @@
|
||||
Features:
|
||||
- Search is accessible through a new icon on the main screen
|
||||
- Modernize Back Handling
|
||||
- Reenable R8 Code minification
|
||||
- Add a "Play Random Songs" shortcut
|
||||
|
||||
Bug fixes:
|
||||
- Readd the "Star" button to the Now Playing screen
|
||||
- Fix a rare crash when shuffling playlists with duplicate entries
|
||||
- Fix a crash when choosing "Play next" on an empty playlist.
|
||||
- Tracks buttons flash a scrollbar sometimes in Android 13
|
||||
- Fix EndlessScrolling in genre listing
|
||||
- Couldn't delete a track when shuffle was active
|
||||
- Upgrade material to 1.9.0
|
||||
|
@ -6,7 +6,7 @@ navigation = "2.6.0"
|
||||
gradlePlugin = "8.0.2"
|
||||
androidxcore = "1.10.1"
|
||||
ktlint = "0.43.2"
|
||||
ktlintGradle = "11.4.0"
|
||||
ktlintGradle = "11.4.2"
|
||||
detekt = "1.23.0"
|
||||
preferences = "1.2.0"
|
||||
media3 = "1.0.2"
|
||||
@ -15,7 +15,7 @@ androidSupport = "1.6.0"
|
||||
materialDesign = "1.9.0"
|
||||
constraintLayout = "2.1.4"
|
||||
multidex = "2.0.1"
|
||||
room = "2.5.1"
|
||||
room = "2.5.2"
|
||||
kotlin = "1.8.22"
|
||||
kotlinxCoroutines = "1.7.1"
|
||||
viewModelKtx = "2.6.1"
|
||||
@ -30,10 +30,10 @@ picasso = "2.8"
|
||||
|
||||
junit4 = "4.13.2"
|
||||
junit5 = "5.9.3"
|
||||
mockito = "5.3.1"
|
||||
mockito = "5.4.0"
|
||||
mockitoKotlin = "5.0.0"
|
||||
kluent = "1.73"
|
||||
apacheCodecs = "1.15"
|
||||
apacheCodecs = "1.16.0"
|
||||
robolectric = "4.10.3"
|
||||
timber = "5.0.1"
|
||||
fastScroll = "2.0.1"
|
||||
|
@ -9,8 +9,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "org.moire.ultrasonic"
|
||||
versionCode 123
|
||||
versionName "4.6.0"
|
||||
versionCode 124
|
||||
versionName "4.6.1-RC"
|
||||
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
|
@ -26,7 +26,8 @@ import timber.log.Timber
|
||||
* It should be kept generic enough that it can be used a Base for all lists in the app.
|
||||
*/
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
class BaseAdapter<T : Identifiable> : MultiTypeAdapter(), FastScrollRecyclerView.SectionedAdapter {
|
||||
class BaseAdapter<T : Identifiable>(allowDuplicateEntries: Boolean = false) :
|
||||
MultiTypeAdapter(), FastScrollRecyclerView.SectionedAdapter {
|
||||
|
||||
// Update the BoundedTreeSet if selection type is changed
|
||||
internal var selectionType: SelectionType = SelectionType.MULTIPLE
|
||||
@ -41,7 +42,7 @@ class BaseAdapter<T : Identifiable> : MultiTypeAdapter(), FastScrollRecyclerView
|
||||
private val diffCallback = GenericDiffCallback<T>()
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
setHasStableIds(!allowDuplicateEntries)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
|
@ -43,14 +43,7 @@ class TrackViewBinder(
|
||||
override fun onBindViewHolder(holder: TrackViewHolder, item: Identifiable) {
|
||||
val diffAdapter = adapter as BaseAdapter<*>
|
||||
|
||||
val track: Track = when (item) {
|
||||
is Track -> {
|
||||
item
|
||||
}
|
||||
else -> {
|
||||
return
|
||||
}
|
||||
}
|
||||
val track = (item as? Track) ?: return
|
||||
|
||||
// Remove observer before binding
|
||||
holder.observableChecked.removeObservers(lifecycleOwner)
|
||||
@ -59,7 +52,7 @@ class TrackViewBinder(
|
||||
song = track,
|
||||
checkable = checkable,
|
||||
draggable = draggable,
|
||||
diffAdapter.isSelected(item.longId)
|
||||
diffAdapter.isSelected(track.longId)
|
||||
)
|
||||
|
||||
holder.itemView.setOnLongClickListener {
|
||||
@ -110,7 +103,7 @@ class TrackViewBinder(
|
||||
diffAdapter.selectionRevision.observe(
|
||||
lifecycleOwner
|
||||
) {
|
||||
val newStatus = diffAdapter.isSelected(item.longId)
|
||||
val newStatus = diffAdapter.isSelected(track.longId)
|
||||
|
||||
if (newStatus != holder.check.isChecked) holder.check.isChecked = newStatus
|
||||
}
|
||||
|
@ -135,7 +135,6 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||
item.id,
|
||||
append = false,
|
||||
autoPlay = true,
|
||||
shuffle = false,
|
||||
playNext = false,
|
||||
isArtist = isArtist
|
||||
)
|
||||
@ -145,7 +144,6 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||
item.id,
|
||||
append = false,
|
||||
autoPlay = true,
|
||||
shuffle = true,
|
||||
playNext = true,
|
||||
isArtist = isArtist
|
||||
)
|
||||
@ -155,7 +153,6 @@ abstract class EntryListFragment<T : GenericEntry> : MultiListFragment<T>() {
|
||||
item.id,
|
||||
append = true,
|
||||
autoPlay = false,
|
||||
shuffle = false,
|
||||
playNext = false,
|
||||
isArtist = isArtist
|
||||
)
|
||||
|
@ -176,7 +176,7 @@ class PlayerFragment :
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val viewAdapter: BaseAdapter<Identifiable> by lazy {
|
||||
BaseAdapter()
|
||||
BaseAdapter(allowDuplicateEntries = true)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -536,8 +536,10 @@ class PlayerFragment :
|
||||
if (update.success == true && update.rating is HeartRating) {
|
||||
if (update.rating.isHeart) {
|
||||
starMenuItem.setIcon(fullStar)
|
||||
starMenuItem.setTitle(R.string.download_menu_unstar)
|
||||
} else {
|
||||
starMenuItem.setIcon(hollowStar)
|
||||
starMenuItem.setTitle(R.string.download_menu_star)
|
||||
}
|
||||
} else if (update.success == false) {
|
||||
Toast.makeText(context, "Setting rating failed", Toast.LENGTH_SHORT)
|
||||
|
@ -316,7 +316,6 @@ open class TrackCollectionFragment(
|
||||
append = append,
|
||||
playNext = false,
|
||||
autoPlay = !append,
|
||||
shuffle = false,
|
||||
playlistName = null,
|
||||
fragment = this
|
||||
)
|
||||
@ -616,7 +615,6 @@ open class TrackCollectionFragment(
|
||||
append = true,
|
||||
playNext = true,
|
||||
autoPlay = false,
|
||||
shuffle = false,
|
||||
playlistName = navArgs.playlistName,
|
||||
fragment = this@TrackCollectionFragment
|
||||
)
|
||||
|
@ -429,7 +429,10 @@ class MediaPlayerManager(
|
||||
when (insertionMode) {
|
||||
InsertionMode.CLEAR -> clear()
|
||||
InsertionMode.APPEND -> insertAt = mediaItemCount
|
||||
InsertionMode.AFTER_CURRENT -> insertAt = currentMediaItemIndex + 1
|
||||
InsertionMode.AFTER_CURRENT -> {
|
||||
// Must never be larger than the count of items (especially when empty)
|
||||
insertAt = (currentMediaItemIndex + 1).coerceAtMost(mediaItemCount)
|
||||
}
|
||||
}
|
||||
|
||||
val mediaItems: List<MediaItem> = songs.map {
|
||||
@ -437,10 +440,13 @@ class MediaPlayerManager(
|
||||
result
|
||||
}
|
||||
|
||||
if (shuffle) isShufflePlayEnabled = true
|
||||
Timber.w("Adding ${mediaItems.size} media items")
|
||||
controller?.addMediaItems(insertAt, mediaItems)
|
||||
|
||||
// There is a bug in media3 ( https://github.com/androidx/media/issues/480 ),
|
||||
// so we must first add the tracks, and then enable shuffle
|
||||
if (shuffle) isShufflePlayEnabled = true
|
||||
|
||||
prepare()
|
||||
|
||||
// Playback doesn't start correctly when the player is in STATE_ENDED.
|
||||
|
@ -98,7 +98,7 @@ class DownloadHandler(
|
||||
isDirectory: Boolean = true,
|
||||
append: Boolean,
|
||||
autoPlay: Boolean,
|
||||
shuffle: Boolean,
|
||||
shuffle: Boolean = false,
|
||||
playNext: Boolean,
|
||||
isArtist: Boolean = false
|
||||
) {
|
||||
@ -141,7 +141,7 @@ class DownloadHandler(
|
||||
append: Boolean,
|
||||
playNext: Boolean,
|
||||
autoPlay: Boolean,
|
||||
shuffle: Boolean,
|
||||
shuffle: Boolean = false,
|
||||
playlistName: String? = null,
|
||||
fragment: Fragment
|
||||
) {
|
||||
|
@ -12,7 +12,7 @@
|
||||
<item
|
||||
a:id="@+id/menu_item_star"
|
||||
a:icon="@drawable/ic_star_hollow"
|
||||
app:showAsAction="ifRoom|withText"
|
||||
app:showAsAction="always"
|
||||
a:title="@string/download.menu_star"/>
|
||||
|
||||
<item
|
||||
|
@ -7,6 +7,6 @@
|
||||
a:icon="@drawable/ic_menu_search"
|
||||
a:title="@string/button_bar.search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always|collapseActionView" />
|
||||
app:showAsAction="ifRoom|collapseActionView" />
|
||||
|
||||
</menu>
|
@ -44,7 +44,7 @@
|
||||
<string name="common.play_next">Reproducir a continuación</string>
|
||||
<string name="common.play_now">Reproducir ahora</string>
|
||||
<string name="common.play_shuffled">Reproducción aleatoria</string>
|
||||
<string name="common.public">Public</string>
|
||||
<string name="common.public">Público</string>
|
||||
<string name="common.save">Guardar</string>
|
||||
<string name="common.select_all">Seleccionar todo</string>
|
||||
<string name="common.title">Título</string>
|
||||
@ -57,14 +57,14 @@
|
||||
<string name="download.bookmark_set_at_position" formatted="false">Marcador añadido a %s.</string>
|
||||
<string name="download.empty">Nada se esta descargando</string>
|
||||
<string name="playlist.empty">La lista de reproducción esta vacía</string>
|
||||
<string name="download.jukebox_not_authorized">El control remoto no esta habilitado. Por favor habilita el modo jukebox en <b>Configuración > Usuarios</b> en tu servidor de Subsonic.</string>
|
||||
<string name="download.jukebox_not_authorized">El control remoto no esta habilitado. Por favor habilita el modo gramola (jukebox) en <b>Configuración > Usuarios</b> en tu servidor de Subsonic.</string>
|
||||
<string name="download.jukebox_off">Control remoto apagado. La música se reproduce en tu dispositivo.</string>
|
||||
<string name="download.jukebox_offline">Control remoto no disponible en modo fuera de línea.</string>
|
||||
<string name="download.jukebox_on">Control remoto encendido. La música se reproduce en el servidor.</string>
|
||||
<string name="download.jukebox_server_too_old">Control remoto no soportado. Por favor actualiza tu servidor de Subsonic.</string>
|
||||
<string name="download.menu_equalizer">Ecualizador</string>
|
||||
<string name="download.menu_jukebox_off">Apagar Jukebox</string>
|
||||
<string name="download.menu_jukebox_on">Encender Jukebox</string>
|
||||
<string name="download.menu_jukebox_off">Apagar gramola</string>
|
||||
<string name="download.menu_jukebox_on">Encender gramola</string>
|
||||
<string name="download.menu_lyrics">Letras</string>
|
||||
<string name="download.menu_save">Guardar lista de reproducción</string>
|
||||
<string name="download.menu_screen_off">Pantalla apagada</string>
|
||||
@ -87,7 +87,7 @@
|
||||
<string name="equalizer.label">Ecualizador</string>
|
||||
<string name="equalizer.preset">Seleccionar preajuste</string>
|
||||
<string name="error.label">Error</string>
|
||||
<string name="jukebox.is_default">Jukebox por defecto</string>
|
||||
<string name="jukebox.is_default">Gramola por defecto</string>
|
||||
<string name="lyrics.nomatch">No se encontraron letras</string>
|
||||
<string name="language.default">Predeterminado del sistema</string>
|
||||
<string name="language.zh_CN">Chino (China)</string>
|
||||
@ -446,7 +446,7 @@
|
||||
<string name="settings.preload_100">100 canciones</string>
|
||||
<string name="settings.preload_50">50 canciones</string>
|
||||
<string name="settings.preload_1000">1000 canciones</string>
|
||||
<string name="jukebox">Jukebox</string>
|
||||
<string name="jukebox">Gramola</string>
|
||||
<string name="settings.preload_500">500 canciones</string>
|
||||
<string name="supported_server_features">Funciones soportadas</string>
|
||||
<string name="foreground_exception_title">No se puede reanudar la reproducción</string>
|
||||
@ -454,4 +454,5 @@
|
||||
<string name="settings.max_bitrate_pinning">Tasa de bits máxima: al fijar una canción de forma permanente</string>
|
||||
<string name="shortcut_play_random_songs_short">Canciones aleatorias</string>
|
||||
<string name="shortcut_play_random_songs_long">Reproducir las canciones aleatoriamente</string>
|
||||
<string name="download.menu_unstar">No me gusta</string>
|
||||
</resources>
|
@ -24,4 +24,40 @@
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.search">Buscar</string>
|
||||
<string name="chat.send_a_message">Enviar unha mensaxe</string>
|
||||
<string name="button_bar.bookmarks">Marcadores</string>
|
||||
<string name="chat.send_button">Enviar</string>
|
||||
<string name="chat.user_avatar">Imaxe do avatar</string>
|
||||
<string name="common.album">Álbum</string>
|
||||
<string name="common.comment">Comentar</string>
|
||||
<string name="common.confirm">Confirmar</string>
|
||||
<string name="common.delete">Borrar</string>
|
||||
<string name="common.download">Descargar</string>
|
||||
<string name="common.info">Detalles</string>
|
||||
<string name="common.multiple_genres">Múltiples xéneros</string>
|
||||
<string name="common.name">Nome</string>
|
||||
<string name="common.ok">OK</string>
|
||||
<string name="common.play_next">Reproducir a continuación</string>
|
||||
<string name="common.play_now">Reproducir agora</string>
|
||||
<string name="common.play_shuffled">Reproducir aleatoriamente</string>
|
||||
<string name="common.public">Público</string>
|
||||
<string name="common.save">Gardar</string>
|
||||
<string name="common.select_all">Seleccionar todo</string>
|
||||
<string name="common.unpin">Desancorar</string>
|
||||
<string name="common.unpin_selection_confirmation">¿Realmente queres desancorar a selección actual\?</string>
|
||||
<string name="common.appname">Ultrasonic</string>
|
||||
<string name="common.title">Título</string>
|
||||
<string name="common.artist">Artista</string>
|
||||
<string name="common.cancel">Cancelar</string>
|
||||
<string name="common.play_last">Reproducir última</string>
|
||||
<string name="common.pin">Ancorar</string>
|
||||
<string name="settings.show_confirmation_dialog_summary">Mostra un cadro de diálogo de confirmación antes de eliminar ou desancorar as cancións</string>
|
||||
<plurals name="select_album_n_songs_pinned">
|
||||
<item quantity="one">%d canción seleccionada para ser ancorada</item>
|
||||
<item quantity="other">%d cancións seleccionadas para ser ancoradas</item>
|
||||
</plurals>
|
||||
<string name="settings.max_bitrate_pinning">Tasa de bits máxima: ao fixar unha canción de forma permanente</string>
|
||||
<plurals name="select_album_n_songs_unpinned">
|
||||
<item quantity="one">%d canción desancorada</item>
|
||||
<item quantity="other">%d cancións desancoradas</item>
|
||||
</plurals>
|
||||
</resources>
|
@ -346,6 +346,7 @@
|
||||
<string name="download.bookmark_set">Set Bookmark</string>
|
||||
<string name="download.bookmark_delete">Delete Bookmark</string>
|
||||
<string name="download.menu_star">Star</string>
|
||||
<string name="download.menu_unstar">Unstar</string>
|
||||
<string name="download.menu_clear_playlist">Clear Playlist</string>
|
||||
<string name="button_bar.shares">Shares</string>
|
||||
<string name="select_share.empty">No shares available on server</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user