Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +10 −2 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.app.Notification.ProgressStyle.Segment import android.app.PendingIntent import android.app.Person import android.content.Intent import android.graphics.drawable.Icon import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -38,6 +39,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.row.RowImageInflater import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Test Loading @@ -49,6 +51,8 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { private val kosmos = testKosmos() private val underTest = kosmos.promotedNotificationContentExtractor private val rowImageInflater = RowImageInflater.newInstance(previousIndex = null) private val imageModelProvider by lazy { rowImageInflater.useForContentModel() } @Test @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) Loading Loading @@ -294,14 +298,18 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? { val recoveredBuilder = Notification.Builder(context, entry.sbn.notification) return underTest.extractContent(entry, recoveredBuilder) return underTest.extractContent(entry, recoveredBuilder, imageModelProvider) } private fun createEntry( promoted: Boolean = true, builderBlock: Notification.Builder.() -> Unit = {}, ): NotificationEntry { val notif = Notification.Builder(context, "channel").also(builderBlock).build() val notif = Notification.Builder(context, "channel") .setSmallIcon(Icon.createWithContentUri("content://foo/bar")) .also(builderBlock) .build() if (promoted) { notif.flags = FLAG_PROMOTED_ONGOING } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +1 −0 Original line number Diff line number Diff line Loading @@ -203,6 +203,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { val result = NotificationRowContentBinderImpl.InflationProgress( packageContext = mContext, rowImageInflater = RowImageInflater.newInstance(null), remoteViews = NewRemoteViews(), contentModel = NotificationContentModel(headsUpStatusBarModel), promotedContent = null, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt 0 → 100644 +120 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * 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 com.android.systemui.statusbar.notification.row import android.graphics.drawable.Icon import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod import com.android.systemui.statusbar.notification.row.shared.ImageModel import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.SmallSquare import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) @EnableFlags(PromotedNotificationUiAod.FLAG_NAME) class RowImageInflaterTest : SysuiTestCase() { private lateinit var rowImageInflater: RowImageInflater private val resIcon1 = Icon.createWithResource(context, android.R.drawable.ic_info) private val resIcon2 = Icon.createWithResource(context, android.R.drawable.ic_delete) private val badUriIcon = Icon.createWithContentUri("content://com.test/does_not_exist") @Before fun setUp() { rowImageInflater = RowImageInflater.newInstance(null) } @Test fun getNewImageIndex_returnsNullWhenUnused() { assertThat(rowImageInflater.getNewImageIndex()).isNull() } @Test fun getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() { assertThat(getImageModelsForIcons()).isEmpty() val result = rowImageInflater.getNewImageIndex() assertThat(result).isNotNull() assertThat(result?.contentsForTesting).isEmpty() } @Test fun getNewImageIndex_returnsSingleImageWhenOneImageLoaded() { assertThat(getImageModelsForIcons(resIcon1)).hasSize(1) val result = rowImageInflater.getNewImageIndex() assertThat(result).isNotNull() assertThat(result?.contentsForTesting).hasSize(1) } @Test fun exampleFirstGeneration() { // GIVEN various models are required val providedModels = getImageModelsForIcons(resIcon1, badUriIcon, resIcon1, resIcon2) // VERIFY that we get 4 models, and the two with the same value are shared assertThat(providedModels).hasSize(4) assertThat(providedModels[0]).isNotSameInstanceAs(providedModels[1]) assertThat(providedModels[0]).isSameInstanceAs(providedModels[2]) assertThat(providedModels[0]).isNotSameInstanceAs(providedModels[3]) // THEN load images rowImageInflater.loadImagesSynchronously(context) // VERIFY that the valid drawables are loaded assertThat(providedModels[0].drawable).isNotNull() assertThat(providedModels[1].drawable).isNull() assertThat(providedModels[2].drawable).isNotNull() assertThat(providedModels[3].drawable).isNotNull() // VERIFY the returned index has all 3 entries, 2 of which have drawables val indexGen1 = rowImageInflater.getNewImageIndex() assertThat(indexGen1).isNotNull() assertThat(indexGen1?.contentsForTesting).hasSize(3) assertThat(indexGen1?.contentsForTesting?.mapNotNull { it.drawable }).hasSize(2) } @Test fun exampleSecondGeneration_whichLoadsNothing() { exampleFirstGeneration() // THEN start a new generation of the inflation rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex()) getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() } @Test fun exampleSecondGeneration_whichLoadsOneImage() { exampleFirstGeneration() // THEN start a new generation of the inflation rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex()) getNewImageIndex_returnsSingleImageWhenOneImageLoaded() } private fun getImageModelsForIcons(vararg icons: Icon): List<ImageModel> { val provider = rowImageInflater.useForContentModel() return icons.map { icon -> requireNotNull(provider.getImageModel(icon, SmallSquare)) { "null model for $icon" } } } } packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +1 −1 Original line number Diff line number Diff line Loading @@ -322,7 +322,7 @@ public interface NotificationsModule { if (PromotedNotificationContentModel.featureFlagEnabled()) { return implProvider.get(); } else { return (entry, recoveredBuilder) -> null; return (entry, recoveredBuilder, imageModelProvider) -> null; } } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +24 −9 Original line number Diff line number Diff line Loading @@ -55,12 +55,15 @@ import com.android.internal.widget.NotificationProgressModel import com.android.internal.widget.NotificationRowIconView import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.res.R as systemuiR import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.Background import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.PrimaryText import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.SecondaryText import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When import com.android.systemui.statusbar.notification.promoted.ui.viewmodel.AODPromotedNotificationViewModel import com.android.systemui.statusbar.notification.row.shared.ImageModel import com.android.systemui.statusbar.notification.row.shared.isNullOrEmpty @Composable fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.Factory) { Loading Loading @@ -171,6 +174,7 @@ private class AODPromotedNotificationViewUpdater(root: View) { root.findViewById(R.id.header_text_secondary_divider) private val icon: NotificationRowIconView? = root.findViewById(R.id.icon) private val leftIcon: ImageView? = root.findViewById(R.id.left_icon) private val rightIcon: ImageView? = root.findViewById(R.id.right_icon) private val notificationProgressEndIcon: CachingIconView? = root.findViewById(R.id.notification_progress_end_icon) private val notificationProgressStartIcon: CachingIconView? = Loading Loading @@ -224,10 +228,16 @@ private class AODPromotedNotificationViewUpdater(root: View) { textView: ImageFloatingTextView? = null, showOldProgress: Boolean = true, ) { // Icon binding must be called in this order updateImageView(icon, content.smallIcon) icon?.setBackgroundColor(Background.colorInt) icon?.originalIconColor = PrimaryText.colorInt updateHeader(content, hideTitle = true) updateTitle(title, content) updateText(textView ?: text, content) updateImageView(rightIcon, content.skeletonLargeIcon) if (showOldProgress) { updateOldProgressBar(content) Loading Loading @@ -327,6 +337,7 @@ private class AODPromotedNotificationViewUpdater(root: View) { updateTimeAndChronometer(content) updateConversationHeaderDividers(content, hideTitle = true) updateImageView(verificationIcon, content.verificationIcon) updateTextView(verificationText, content.verificationText) } Loading @@ -337,7 +348,8 @@ private class AODPromotedNotificationViewUpdater(root: View) { val hasTitle = content.title != null && !hideTitle val hasAppName = content.appName != null val hasTimeOrChronometer = content.time != null val hasVerification = content.verificationIcon != null || content.verificationText != null val hasVerification = !content.verificationIcon.isNullOrEmpty() || !content.verificationText.isNullOrEmpty() val hasTextBeforeAppName = hasTitle val hasTextBeforeTime = hasTitle || hasAppName Loading Loading @@ -415,15 +427,18 @@ private class AODPromotedNotificationViewUpdater(root: View) { text: CharSequence?, color: AodPromotedNotificationColor = SecondaryText, ) { if (view == null) return setTextViewColor(view, color) if (text != null && text.isNotEmpty()) { view?.text = text.toSkeleton() view?.visibility = VISIBLE } else { view?.text = "" view?.visibility = GONE view.text = text?.toSkeleton() ?: "" view.isVisible = !text.isNullOrEmpty() } private fun updateImageView(view: ImageView?, model: ImageModel?) { if (view == null) return val drawable = model?.drawable view.setImageDrawable(drawable) view.isVisible = drawable != null } private fun setTextViewColor(view: TextView?, color: AodPromotedNotificationColor) { Loading Loading @@ -464,7 +479,7 @@ private fun Notification.ProgressStyle.Point.toSkeleton(): Notification.Progress } private enum class AodPromotedNotificationColor(colorUInt: UInt) { Background(0x00000000u), Background(0xFF000000u), PrimaryText(0xFFFFFFFFu), SecondaryText(0xFFCCCCCCu); Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +10 −2 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.app.Notification.ProgressStyle.Segment import android.app.PendingIntent import android.app.Person import android.content.Intent import android.graphics.drawable.Icon import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -38,6 +39,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.row.RowImageInflater import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Test Loading @@ -49,6 +51,8 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { private val kosmos = testKosmos() private val underTest = kosmos.promotedNotificationContentExtractor private val rowImageInflater = RowImageInflater.newInstance(previousIndex = null) private val imageModelProvider by lazy { rowImageInflater.useForContentModel() } @Test @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) Loading Loading @@ -294,14 +298,18 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? { val recoveredBuilder = Notification.Builder(context, entry.sbn.notification) return underTest.extractContent(entry, recoveredBuilder) return underTest.extractContent(entry, recoveredBuilder, imageModelProvider) } private fun createEntry( promoted: Boolean = true, builderBlock: Notification.Builder.() -> Unit = {}, ): NotificationEntry { val notif = Notification.Builder(context, "channel").also(builderBlock).build() val notif = Notification.Builder(context, "channel") .setSmallIcon(Icon.createWithContentUri("content://foo/bar")) .also(builderBlock) .build() if (promoted) { notif.flags = FLAG_PROMOTED_ONGOING } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +1 −0 Original line number Diff line number Diff line Loading @@ -203,6 +203,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { val result = NotificationRowContentBinderImpl.InflationProgress( packageContext = mContext, rowImageInflater = RowImageInflater.newInstance(null), remoteViews = NewRemoteViews(), contentModel = NotificationContentModel(headsUpStatusBarModel), promotedContent = null, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/RowImageInflaterTest.kt 0 → 100644 +120 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * 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 com.android.systemui.statusbar.notification.row import android.graphics.drawable.Icon import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod import com.android.systemui.statusbar.notification.row.shared.ImageModel import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.SmallSquare import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) @EnableFlags(PromotedNotificationUiAod.FLAG_NAME) class RowImageInflaterTest : SysuiTestCase() { private lateinit var rowImageInflater: RowImageInflater private val resIcon1 = Icon.createWithResource(context, android.R.drawable.ic_info) private val resIcon2 = Icon.createWithResource(context, android.R.drawable.ic_delete) private val badUriIcon = Icon.createWithContentUri("content://com.test/does_not_exist") @Before fun setUp() { rowImageInflater = RowImageInflater.newInstance(null) } @Test fun getNewImageIndex_returnsNullWhenUnused() { assertThat(rowImageInflater.getNewImageIndex()).isNull() } @Test fun getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() { assertThat(getImageModelsForIcons()).isEmpty() val result = rowImageInflater.getNewImageIndex() assertThat(result).isNotNull() assertThat(result?.contentsForTesting).isEmpty() } @Test fun getNewImageIndex_returnsSingleImageWhenOneImageLoaded() { assertThat(getImageModelsForIcons(resIcon1)).hasSize(1) val result = rowImageInflater.getNewImageIndex() assertThat(result).isNotNull() assertThat(result?.contentsForTesting).hasSize(1) } @Test fun exampleFirstGeneration() { // GIVEN various models are required val providedModels = getImageModelsForIcons(resIcon1, badUriIcon, resIcon1, resIcon2) // VERIFY that we get 4 models, and the two with the same value are shared assertThat(providedModels).hasSize(4) assertThat(providedModels[0]).isNotSameInstanceAs(providedModels[1]) assertThat(providedModels[0]).isSameInstanceAs(providedModels[2]) assertThat(providedModels[0]).isNotSameInstanceAs(providedModels[3]) // THEN load images rowImageInflater.loadImagesSynchronously(context) // VERIFY that the valid drawables are loaded assertThat(providedModels[0].drawable).isNotNull() assertThat(providedModels[1].drawable).isNull() assertThat(providedModels[2].drawable).isNotNull() assertThat(providedModels[3].drawable).isNotNull() // VERIFY the returned index has all 3 entries, 2 of which have drawables val indexGen1 = rowImageInflater.getNewImageIndex() assertThat(indexGen1).isNotNull() assertThat(indexGen1?.contentsForTesting).hasSize(3) assertThat(indexGen1?.contentsForTesting?.mapNotNull { it.drawable }).hasSize(2) } @Test fun exampleSecondGeneration_whichLoadsNothing() { exampleFirstGeneration() // THEN start a new generation of the inflation rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex()) getNewImageIndex_returnsEmptyIndexWhenZeroImagesLoaded() } @Test fun exampleSecondGeneration_whichLoadsOneImage() { exampleFirstGeneration() // THEN start a new generation of the inflation rowImageInflater = RowImageInflater.newInstance(rowImageInflater.getNewImageIndex()) getNewImageIndex_returnsSingleImageWhenOneImageLoaded() } private fun getImageModelsForIcons(vararg icons: Icon): List<ImageModel> { val provider = rowImageInflater.useForContentModel() return icons.map { icon -> requireNotNull(provider.getImageModel(icon, SmallSquare)) { "null model for $icon" } } } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +1 −1 Original line number Diff line number Diff line Loading @@ -322,7 +322,7 @@ public interface NotificationsModule { if (PromotedNotificationContentModel.featureFlagEnabled()) { return implProvider.get(); } else { return (entry, recoveredBuilder) -> null; return (entry, recoveredBuilder, imageModelProvider) -> null; } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +24 −9 Original line number Diff line number Diff line Loading @@ -55,12 +55,15 @@ import com.android.internal.widget.NotificationProgressModel import com.android.internal.widget.NotificationRowIconView import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.res.R as systemuiR import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.Background import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.PrimaryText import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.SecondaryText import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When import com.android.systemui.statusbar.notification.promoted.ui.viewmodel.AODPromotedNotificationViewModel import com.android.systemui.statusbar.notification.row.shared.ImageModel import com.android.systemui.statusbar.notification.row.shared.isNullOrEmpty @Composable fun AODPromotedNotification(viewModelFactory: AODPromotedNotificationViewModel.Factory) { Loading Loading @@ -171,6 +174,7 @@ private class AODPromotedNotificationViewUpdater(root: View) { root.findViewById(R.id.header_text_secondary_divider) private val icon: NotificationRowIconView? = root.findViewById(R.id.icon) private val leftIcon: ImageView? = root.findViewById(R.id.left_icon) private val rightIcon: ImageView? = root.findViewById(R.id.right_icon) private val notificationProgressEndIcon: CachingIconView? = root.findViewById(R.id.notification_progress_end_icon) private val notificationProgressStartIcon: CachingIconView? = Loading Loading @@ -224,10 +228,16 @@ private class AODPromotedNotificationViewUpdater(root: View) { textView: ImageFloatingTextView? = null, showOldProgress: Boolean = true, ) { // Icon binding must be called in this order updateImageView(icon, content.smallIcon) icon?.setBackgroundColor(Background.colorInt) icon?.originalIconColor = PrimaryText.colorInt updateHeader(content, hideTitle = true) updateTitle(title, content) updateText(textView ?: text, content) updateImageView(rightIcon, content.skeletonLargeIcon) if (showOldProgress) { updateOldProgressBar(content) Loading Loading @@ -327,6 +337,7 @@ private class AODPromotedNotificationViewUpdater(root: View) { updateTimeAndChronometer(content) updateConversationHeaderDividers(content, hideTitle = true) updateImageView(verificationIcon, content.verificationIcon) updateTextView(verificationText, content.verificationText) } Loading @@ -337,7 +348,8 @@ private class AODPromotedNotificationViewUpdater(root: View) { val hasTitle = content.title != null && !hideTitle val hasAppName = content.appName != null val hasTimeOrChronometer = content.time != null val hasVerification = content.verificationIcon != null || content.verificationText != null val hasVerification = !content.verificationIcon.isNullOrEmpty() || !content.verificationText.isNullOrEmpty() val hasTextBeforeAppName = hasTitle val hasTextBeforeTime = hasTitle || hasAppName Loading Loading @@ -415,15 +427,18 @@ private class AODPromotedNotificationViewUpdater(root: View) { text: CharSequence?, color: AodPromotedNotificationColor = SecondaryText, ) { if (view == null) return setTextViewColor(view, color) if (text != null && text.isNotEmpty()) { view?.text = text.toSkeleton() view?.visibility = VISIBLE } else { view?.text = "" view?.visibility = GONE view.text = text?.toSkeleton() ?: "" view.isVisible = !text.isNullOrEmpty() } private fun updateImageView(view: ImageView?, model: ImageModel?) { if (view == null) return val drawable = model?.drawable view.setImageDrawable(drawable) view.isVisible = drawable != null } private fun setTextViewColor(view: TextView?, color: AodPromotedNotificationColor) { Loading Loading @@ -464,7 +479,7 @@ private fun Notification.ProgressStyle.Point.toSkeleton(): Notification.Progress } private enum class AodPromotedNotificationColor(colorUInt: UInt) { Background(0x00000000u), Background(0xFF000000u), PrimaryText(0xFFFFFFFFu), SecondaryText(0xFFCCCCCCu); Loading