diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt index 8722bae0..f5b4b4e4 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumListFragment.kt @@ -61,10 +61,10 @@ class AlbumListFragment : GenericListFragment onItemClick(entry) }, { menuItem, entry -> onContextMenuItemSelected(menuItem, entry) }, - imageLoaderProvider.getImageLoader() + imageLoaderProvider.getImageLoader(), + onMusicFolderUpdate ) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt index 9785c9e5..2a5f1c46 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt @@ -7,10 +7,8 @@ package org.moire.ultrasonic.fragment -import android.view.LayoutInflater import android.view.MenuItem import android.view.View -import android.view.ViewGroup import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView @@ -18,22 +16,21 @@ import androidx.recyclerview.widget.RecyclerView import org.moire.ultrasonic.R import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.util.ImageLoader -import org.moire.ultrasonic.view.SelectMusicFolderView /** * Creates a Row in a RecyclerView which contains the details of an Album */ class AlbumRowAdapter( albumList: List, - private var selectFolderHeader: SelectMusicFolderView?, onItemClick: (MusicDirectory.Entry) -> Unit, onContextMenuClick: (MenuItem, MusicDirectory.Entry) -> Boolean, - private val imageLoader: ImageLoader + private val imageLoader: ImageLoader, + onMusicFolderUpdate: (String?) -> Unit ) : GenericRowAdapter( - selectFolderHeader, onItemClick, onContextMenuClick, - imageLoader + imageLoader, + onMusicFolderUpdate ) { override var itemList = albumList @@ -48,20 +45,8 @@ class AlbumRowAdapter( super.notifyDataSetChanged() } - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): RecyclerView.ViewHolder { - if (viewType == TYPE_ITEM) { - val row = LayoutInflater.from(parent.context) - .inflate(layout, parent, false) - return AlbumViewHolder(row) - } - return selectFolderHeader!! - } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - if (holder is AlbumViewHolder) { + if (holder is ViewHolder) { val listPosition = if (selectFolderHeader != null) position - 1 else position val entry = itemList[listPosition] holder.album.text = entry.title @@ -73,7 +58,7 @@ class AlbumRowAdapter( imageLoader.loadImage( holder.coverArt, MusicDirectory.Entry().apply { coverArt = holder.coverArtId }, - false, 0, false, true, R.drawable.ic_contact_picture + false, 0, false, true, R.drawable.unknown_album ) } } @@ -88,13 +73,20 @@ class AlbumRowAdapter( /** * Holds the view properties of an Item row */ - class AlbumViewHolder( - itemView: View - ) : RecyclerView.ViewHolder(itemView) { - var album: TextView = itemView.findViewById(R.id.album_title) - var artist: TextView = itemView.findViewById(R.id.album_artist) - var details: LinearLayout = itemView.findViewById(R.id.row_album_details) - var coverArt: ImageView = itemView.findViewById(R.id.album_coverart) + class ViewHolder( + view: View + ) : RecyclerView.ViewHolder(view) { + var album: TextView = view.findViewById(R.id.album_title) + var artist: TextView = view.findViewById(R.id.album_artist) + var details: LinearLayout = view.findViewById(R.id.row_album_details) + var coverArt: ImageView = view.findViewById(R.id.album_coverart) var coverArtId: String? = null } + + /** + * Creates an instance of our ViewHolder class + */ + override fun newViewHolder(view: View): RecyclerView.ViewHolder { + return ViewHolder(view) + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt index 2f8e78a6..ddda850b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistListFragment.kt @@ -54,10 +54,10 @@ class ArtistListFragment : GenericListFragment() { override val viewAdapter: ArtistRowAdapter by lazy { ArtistRowAdapter( liveDataItems.value ?: listOf(), - selectFolderHeader, { entry -> onItemClick(entry) }, { menuItem, entry -> onContextMenuItemSelected(menuItem, entry) }, - imageLoaderProvider.getImageLoader() + imageLoaderProvider.getImageLoader(), + onMusicFolderUpdate ) } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt index a021b606..dfd86ef9 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt @@ -17,22 +17,21 @@ import org.moire.ultrasonic.domain.Artist import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.util.ImageLoader import org.moire.ultrasonic.util.Util -import org.moire.ultrasonic.view.SelectMusicFolderView /** * Creates a Row in a RecyclerView which contains the details of an Artist */ class ArtistRowAdapter( artistList: List, - private var selectFolderHeader: SelectMusicFolderView?, onItemClick: (Artist) -> Unit, onContextMenuClick: (MenuItem, Artist) -> Boolean, - private val imageLoader: ImageLoader + private val imageLoader: ImageLoader, + onMusicFolderUpdate: (String?) -> Unit ) : GenericRowAdapter( - selectFolderHeader, onItemClick, onContextMenuClick, - imageLoader + imageLoader, + onMusicFolderUpdate ), SectionedAdapter { @@ -51,7 +50,7 @@ class ArtistRowAdapter( } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - if (holder is ItemViewHolder) { + if (holder is ViewHolder) { val listPosition = if (selectFolderHeader != null) position - 1 else position holder.textView.text = itemList[listPosition].name holder.section.text = getSectionForArtist(listPosition) @@ -101,4 +100,11 @@ class ArtistRowAdapter( if (!section.isLetter()) section = '#' return section.toString() } + + /** + * Creates an instance of our ViewHolder class + */ + override fun newViewHolder(view: View): RecyclerView.ViewHolder { + return ViewHolder(view) + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListFragment.kt index a0b0021e..723193ad 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericListFragment.kt @@ -88,15 +88,25 @@ abstract class GenericListFragment> /** * The observer to be called if the available music folders have changed */ - val musicFolderObserver = { changedFolders: List -> - viewAdapter.notifyDataSetChanged() - selectFolderHeader?.setData( - activeServerProvider.getActiveServer().musicFolderId, - changedFolders - ) + @Suppress("CommentOverPrivateProperty") + private val musicFolderObserver = { folders: List -> + viewAdapter.setFolderList(folders, listModel.activeServer.musicFolderId) Unit } + /** + * What to do when the user has modified the folder filter + */ + val onMusicFolderUpdate = { selectedFolderId: String? -> + if (!listModel.isOffline()) { + val currentSetting = listModel.activeServer + currentSetting.musicFolderId = selectedFolderId + serverSettingsModel.updateItem(currentSetting) + } + viewAdapter.notifyDataSetChanged() + listModel.refresh(refreshListView!!, arguments) + } + /** * Whether to show the folder selector */ @@ -142,27 +152,15 @@ abstract class GenericListFragment> // Create a View Manager viewManager = LinearLayoutManager(this.context) - // Show folder selector UI if enabled - if (showFolderHeader()) { - selectFolderHeader = SelectMusicFolderView( - requireContext(), view as ViewGroup - ) { selectedFolderId -> - if (!listModel.isOffline()) { - val currentSetting = listModel.activeServer - currentSetting.musicFolderId = selectedFolderId - serverSettingsModel.updateItem(currentSetting) - } - viewAdapter.notifyDataSetChanged() - listModel.refresh(refreshListView!!, arguments) - } - } - // Hook up the view with the manager and the adapter listView = view.findViewById(recyclerViewId).apply { setHasFixedSize(true) layoutManager = viewManager adapter = viewAdapter } + + // Configure whether to show the folder header + viewAdapter.folderHeaderEnabled = showFolderHeader() } @Override diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt index 32714999..414c76d0 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt @@ -19,6 +19,7 @@ import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider +import org.moire.ultrasonic.domain.MusicFolder import org.moire.ultrasonic.util.ImageLoader import org.moire.ultrasonic.view.SelectMusicFolderView @@ -26,16 +27,20 @@ import org.moire.ultrasonic.view.SelectMusicFolderView * An abstract Adapter, which can be extended to display a List of in a RecyclerView */ abstract class GenericRowAdapter( - private var selectFolderHeader: SelectMusicFolderView?, val onItemClick: (T) -> Unit, val onContextMenuClick: (MenuItem, T) -> Boolean, - private val imageLoader: ImageLoader + private val imageLoader: ImageLoader, + private val onMusicFolderUpdate: (String?) -> Unit ) : RecyclerView.Adapter() { - open var itemList: List = listOf() protected abstract val layout: Int protected abstract val contextMenuLayout: Int + var folderHeaderEnabled: Boolean = true + var selectFolderHeader: SelectMusicFolderView? = null + var musicFolders: List = listOf() + var selectedFolder: String? = null + /** * Sets the data to be displayed in the RecyclerView */ @@ -44,6 +49,25 @@ abstract class GenericRowAdapter( notifyDataSetChanged() } + /** + * Sets the content and state of the music folder selector row + */ + fun setFolderList(changedFolders: List, selectedId: String?) { + musicFolders = changedFolders + selectedFolder = selectedId + + selectFolderHeader?.setData( + selectedFolder, + musicFolders + ) + + notifyDataSetChanged() + } + + open fun newViewHolder(view: View): RecyclerView.ViewHolder { + return ViewHolder(view) + } + override fun onCreateViewHolder( parent: ViewGroup, viewType: Int @@ -51,13 +75,27 @@ abstract class GenericRowAdapter( if (viewType == TYPE_ITEM) { val row = LayoutInflater.from(parent.context) .inflate(layout, parent, false) - return ItemViewHolder(row) + return newViewHolder(row) + } else { + val row = LayoutInflater.from(parent.context) + .inflate( + R.layout.select_folder_header, parent, false + ) + selectFolderHeader = SelectMusicFolderView(parent.context, row, onMusicFolderUpdate) + + if (musicFolders.isNotEmpty()) { + selectFolderHeader?.setData( + selectedFolder, + musicFolders + ) + } + + return selectFolderHeader!! } - return selectFolderHeader!! } override fun onViewRecycled(holder: RecyclerView.ViewHolder) { - if ((holder is ItemViewHolder) && (holder.coverArtId != null)) { + if ((holder is ViewHolder) && (holder.coverArtId != null)) { imageLoader.cancel(holder.coverArtId) } super.onViewRecycled(holder) @@ -73,7 +111,7 @@ abstract class GenericRowAdapter( } override fun getItemViewType(position: Int): Int { - return if (position == 0 && selectFolderHeader != null) TYPE_HEADER else TYPE_ITEM + return if (position == 0 && folderHeaderEnabled) TYPE_HEADER else TYPE_ITEM } internal fun createPopupMenu(view: View, position: Int): Boolean { @@ -94,7 +132,7 @@ abstract class GenericRowAdapter( /** * Holds the view properties of an Item row */ - class ItemViewHolder( + class ViewHolder( itemView: View ) : RecyclerView.ViewHolder(itemView) { var section: TextView = itemView.findViewById(R.id.row_section) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt index 8eeaf9c5..b5ecc21f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt @@ -34,13 +34,10 @@ import java.util.Random import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.launch import org.koin.android.ext.android.inject -import org.koin.android.viewmodel.ext.android.viewModel import org.koin.core.component.KoinApiExtension import org.moire.ultrasonic.R -import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline import org.moire.ultrasonic.domain.MusicDirectory -import org.moire.ultrasonic.domain.MusicFolder import org.moire.ultrasonic.fragment.FragmentTitle.Companion.getTitle import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.service.CommunicationErrorHandler @@ -57,7 +54,6 @@ import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.view.AlbumView import org.moire.ultrasonic.view.EntryAdapter -import org.moire.ultrasonic.view.SelectMusicFolderView import org.moire.ultrasonic.view.SongView import timber.log.Timber @@ -71,7 +67,6 @@ class TrackCollectionFragment : Fragment() { private var refreshAlbumListView: SwipeRefreshLayout? = null private var albumListView: ListView? = null private var header: View? = null - private var selectFolderHeader: SelectMusicFolderView? = null private var albumButtons: View? = null private var emptyView: TextView? = null private var selectButton: ImageView? = null @@ -95,7 +90,6 @@ class TrackCollectionFragment : Fragment() { private val imageLoaderProvider: ImageLoaderProvider by inject() private val shareHandler: ShareHandler by inject() private var cancellationToken: CancellationToken? = null - private val activeServerProvider: ActiveServerProvider by inject() private val model: TrackCollectionModel by viewModels() private val random: Random = SecureRandom() @@ -131,19 +125,6 @@ class TrackCollectionFragment : Fragment() { false ) - selectFolderHeader = SelectMusicFolderView( - requireContext(), view as ViewGroup - ) { selectedFolderId -> - if (!isOffline()) { - val serverSettingsModel: ServerSettingsModel by viewModel() - val currentSetting = activeServerProvider.getActiveServer() - currentSetting.musicFolderId = selectedFolderId - serverSettingsModel.updateItem(currentSetting) - } - this.updateDisplay(true) - } - - model.musicFolders.observe(viewLifecycleOwner, musicFolderObserver) model.currentDirectory.observe(viewLifecycleOwner, defaultObserver) model.songsForGenre.observe(viewLifecycleOwner, songsForGenreObserver) @@ -622,15 +603,6 @@ class TrackCollectionFragment : Fragment() { mediaPlayerController.unpin(songs) } - private val musicFolderObserver = Observer> { changedFolders -> - if (changedFolders != null) { - selectFolderHeader!!.setData( - activeServerProvider.getActiveServer().musicFolderId, - changedFolders - ) - } - } - private val songsForGenreObserver = Observer { musicDirectory -> // Hide more button when results are less than album list size @@ -726,12 +698,9 @@ class TrackCollectionFragment : Fragment() { } } } else { - if (model.showSelectFolderHeader(arguments)) { - if (albumListView!!.headerViewsCount == 0) { - albumListView!!.addHeaderView(selectFolderHeader!!.itemView, null, false) - } - } + // TODO: This code path can be removed when getArtist has been moved to + // AlbumListFragment (getArtist returns the albums of an artist) pinButton!!.visibility = View.GONE unpinButton!!.visibility = View.GONE downloadButton!!.visibility = View.GONE diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SelectMusicFolderView.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SelectMusicFolderView.kt index 28d34b2d..3dcec9a4 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SelectMusicFolderView.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/view/SelectMusicFolderView.kt @@ -1,89 +1,87 @@ -package org.moire.ultrasonic.view - -import android.content.Context -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.PopupMenu -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import org.moire.ultrasonic.R -import org.moire.ultrasonic.domain.MusicFolder - -/** - * This little view shows the currently selected Folder (or catalog) on the music server. - * When clicked it will drop down a list of all available Folders and allow you to - * select one. The intended usage is to supply a filter to lists of artists, albums, etc - */ -class SelectMusicFolderView( - private val context: Context, - root: ViewGroup, - private val onUpdate: (String?) -> Unit -) : RecyclerView.ViewHolder( - LayoutInflater.from(context).inflate( - R.layout.select_folder_header, root, false - ) -) { - private var musicFolders: List = mutableListOf() - private var selectedFolderId: String? = null - private val folderName: TextView = itemView.findViewById(R.id.select_folder_name) - private val layout: LinearLayout = itemView.findViewById(R.id.select_folder_header) - private val MENU_GROUP_MUSIC_FOLDER = 10 - - init { - folderName.text = context.getString(R.string.select_artist_all_folders) - layout.setOnClickListener { onFolderClick() } - } - - fun setData(selectedId: String?, folders: List) { - selectedFolderId = selectedId - musicFolders = folders - if (selectedFolderId != null) { - for ((id, name) in musicFolders) { - if (id == selectedFolderId) { - folderName.text = name - break - } - } - } else { - folderName.text = context.getString(R.string.select_artist_all_folders) - } - } - - private fun onFolderClick() { - val popup = PopupMenu(context, layout) - - var menuItem = popup.menu.add( - MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders - ) - if (selectedFolderId == null || selectedFolderId!!.isEmpty()) { - menuItem.isChecked = true - } - musicFolders.forEachIndexed { i, musicFolder -> - val (id, name) = musicFolder - menuItem = popup.menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, name) - if (id == selectedFolderId) { - menuItem.isChecked = true - } - } - - popup.menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true) - - popup.setOnMenuItemClickListener { item -> onFolderMenuItemSelected(item) } - popup.show() - } - - private fun onFolderMenuItemSelected(menuItem: MenuItem): Boolean { - val selectedFolder = if (menuItem.itemId == -1) null else musicFolders[menuItem.itemId] - val musicFolderName = selectedFolder?.name - ?: context.getString(R.string.select_artist_all_folders) - selectedFolderId = selectedFolder?.id - - menuItem.isChecked = true - folderName.text = musicFolderName - onUpdate(selectedFolderId) - - return true - } -} +package org.moire.ultrasonic.view + +import android.content.Context +import android.view.MenuItem +import android.view.View +import android.widget.LinearLayout +import android.widget.PopupMenu +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import org.moire.ultrasonic.R +import org.moire.ultrasonic.domain.MusicFolder + +/** + * This little view shows the currently selected Folder (or catalog) on the music server. + * When clicked it will drop down a list of all available Folders and allow you to + * select one. The intended usage is to supply a filter to lists of artists, albums, etc + */ +class SelectMusicFolderView( + private val context: Context, + view: View, + private val onUpdate: (String?) -> Unit +) : RecyclerView.ViewHolder(view) { + private var musicFolders: List = mutableListOf() + private var selectedFolderId: String? = null + private val folderName: TextView = itemView.findViewById(R.id.select_folder_name) + private val layout: LinearLayout = itemView.findViewById(R.id.select_folder_header) + + init { + folderName.text = context.getString(R.string.select_artist_all_folders) + layout.setOnClickListener { onFolderClick() } + } + + fun setData(selectedId: String?, folders: List) { + selectedFolderId = selectedId + musicFolders = folders + if (selectedFolderId != null) { + for ((id, name) in musicFolders) { + if (id == selectedFolderId) { + folderName.text = name + break + } + } + } else { + folderName.text = context.getString(R.string.select_artist_all_folders) + } + } + + private fun onFolderClick() { + val popup = PopupMenu(context, layout) + + var menuItem = popup.menu.add( + MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders + ) + if (selectedFolderId == null || selectedFolderId!!.isEmpty()) { + menuItem.isChecked = true + } + musicFolders.forEachIndexed { i, musicFolder -> + val (id, name) = musicFolder + menuItem = popup.menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, name) + if (id == selectedFolderId) { + menuItem.isChecked = true + } + } + + popup.menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true) + + popup.setOnMenuItemClickListener { item -> onFolderMenuItemSelected(item) } + popup.show() + } + + private fun onFolderMenuItemSelected(menuItem: MenuItem): Boolean { + val selectedFolder = if (menuItem.itemId == -1) null else musicFolders[menuItem.itemId] + val musicFolderName = selectedFolder?.name + ?: context.getString(R.string.select_artist_all_folders) + selectedFolderId = selectedFolder?.id + + menuItem.isChecked = true + folderName.text = musicFolderName + onUpdate(selectedFolderId) + + return true + } + + companion object { + const val MENU_GROUP_MUSIC_FOLDER = 10 + } +} diff --git a/ultrasonic/src/main/res/drawable-hdpi/unknown_album.png b/ultrasonic/src/main/res/drawable-hdpi/unknown_album.png deleted file mode 100644 index e59b7868..00000000 Binary files a/ultrasonic/src/main/res/drawable-hdpi/unknown_album.png and /dev/null differ diff --git a/ultrasonic/src/main/res/drawable/unknown_album.xml b/ultrasonic/src/main/res/drawable/unknown_album.xml index eae63c63..54f39529 100644 --- a/ultrasonic/src/main/res/drawable/unknown_album.xml +++ b/ultrasonic/src/main/res/drawable/unknown_album.xml @@ -1,12 +1,13 @@ - - + android:width="200dp" + android:height="200dp" + android:viewportWidth="100" + android:viewportHeight="100"> + +