Conversation
|
APK file: https://github.com/nextcloud/android/actions/runs/23901488585/artifacts/6242414452 |
|
APK file: https://github.com/nextcloud/android/actions/runs/23903317034/artifacts/6242813250 |
|
APK file: https://github.com/nextcloud/android/actions/runs/23903829241/artifacts/6243019116 |
|
APK file: https://github.com/nextcloud/android/actions/runs/23904463648/artifacts/6243298905 |
|
APK file: https://github.com/nextcloud/android/actions/runs/23913648608/artifacts/6247279485 |
|
APK file: https://github.com/nextcloud/android/actions/runs/23914107506/artifacts/6247475169 |
|
APK file: https://github.com/nextcloud/android/actions/runs/23915475261/artifacts/6248206586 |
11a0d1c to
216d445
Compare
|
APK file: https://github.com/nextcloud/android/actions/runs/24123009774/artifacts/6322344961 |
|
APK file: https://github.com/nextcloud/android/actions/runs/24123396708/artifacts/6322495416 |
b7c5699 to
7ae80ba
Compare
|
APK file: https://github.com/nextcloud/android/actions/runs/24129902593/artifacts/6325147062 |
7ae80ba to
c801165
Compare
|
APK file: https://github.com/nextcloud/android/actions/runs/24193410737/artifacts/6351393880 |
|
APK file: https://github.com/nextcloud/android/actions/runs/24194734050/artifacts/6351933965 |
|
APK file: https://github.com/nextcloud/android/actions/runs/24238133672/artifacts/6368961516 |
|
APK file: https://github.com/nextcloud/android/actions/runs/24239954726/artifacts/6369704928 |
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
e1afcb3 to
897c281
Compare
|
APK file: https://github.com/nextcloud/android/actions/runs/24249789666/artifacts/6373869333 |
|
blue-Light-Screenshot test failed, but no output was generated. Maybe a preliminary stage failed. |
There was a problem hiding this comment.
Pull request overview
Introduces a new Notifications screen implemented as a NotificationsFragment that can be pushed onto the app’s navigator stack (instead of using a standalone NotificationsActivity), along with Kotlin/coroutine refactors and updated navigation entry points.
Changes:
- Add
NavigatorScreen.Notificationsand a newNotificationsFragmentUI/state implementation. - Route “open notifications” entry points (toolbar button, deep link, push workflow) through
NavigatorActivity. - Convert legacy Java/AsyncTask-based notifications components to Kotlin and coroutines; add new screenshot-based instrumentation tests and screenshots.
Reviewed changes
Copilot reviewed 23 out of 41 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| app/src/main/res/layout/notifications_layout.xml | Restructures notifications layout to support list vs shimmer/empty states with swipe-to-refresh containers. |
| app/src/main/java/com/owncloud/android/ui/notifications/NotificationsContract.kt | Replaces Java contract with Kotlin interface and updated callback signature(s). |
| app/src/main/java/com/owncloud/android/ui/notifications/NotificationsContract.java | Removes old Java contract. |
| app/src/main/java/com/owncloud/android/ui/navigation/NavigatorScreen.kt | Adds Notifications as a navigator screen and marks it as non-drawer. |
| app/src/main/java/com/owncloud/android/ui/navigation/NavigatorActivity.kt | Updates action bar/home behavior and drawer locking for non-drawer screens; introduces view binding usage. |
| app/src/main/java/com/owncloud/android/ui/fragment/notifications/NotificationsFragment.kt | New fragment implementing notifications UI, loading, and actions with coroutines. |
| app/src/main/java/com/owncloud/android/ui/fragment/notifications/NotificationsAdapterItemClick.kt | New adapter callback interface for icon binding, delete, and actions. |
| app/src/main/java/com/owncloud/android/ui/fragment/notifications/model/NotificationsUIState.kt | Introduces sealed UI state model (Loading/Loaded/Empty/Error). |
| app/src/main/java/com/owncloud/android/ui/asynctasks/NotificationExecuteActionTask.kt | Replaces AsyncTask with coroutine-based execution for notification actions. |
| app/src/main/java/com/owncloud/android/ui/asynctasks/NotificationExecuteActionTask.java | Removes old Java AsyncTask implementation. |
| app/src/main/java/com/owncloud/android/ui/asynctasks/DeleteNotificationTask.java | Removes old delete notification AsyncTask. |
| app/src/main/java/com/owncloud/android/ui/asynctasks/DeleteAllNotificationsTask.java | Removes old delete-all AsyncTask. |
| app/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.kt | Kotlin rewrite of notifications adapter, including action buttons and binding. |
| app/src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java | Removes old Java adapter. |
| app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt | Removes standalone NotificationsActivity in favor of fragment-based screen. |
| app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt | Routes notification button to pushFragment(NavigatorScreen.Notifications). |
| app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java | Makes pushFragment public and routes OPEN_NOTIFICATIONS deep link via navigator screen. |
| app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt | Minor whitespace cleanup. |
| app/src/main/java/com/nextcloud/client/jobs/NotificationWork.kt | Routes “open notifications” pending intent to NavigatorActivity. |
| app/src/main/java/com/nextcloud/client/di/ComponentsModule.java | Registers NotificationsFragment for DI and removes NotificationsActivity injector. |
| app/src/main/AndroidManifest.xml | Removes NotificationsActivity from manifest. |
| app/src/androidTest/java/com/owncloud/android/ui/fragment/NotificationsFragmentIT.kt | Adds screenshot-based instrumentation tests for fragment states. |
| app/src/androidTest/java/com/owncloud/android/ui/activity/NotificationsActivityIT.kt | Removes old activity-based screenshot tests. |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_error.png | Adds new screenshot artifact for error state. |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_error_light_white.png | Adds new screenshot artifact for error state (light/white). |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_error_light_black.png | Adds new screenshot artifact for error state (light/black). |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_error_dark_white.png | Adds new screenshot artifact for error state (dark/white). |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_error_dark_blue.png | Adds new screenshot artifact for error state (dark/blue). |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_error_dark_black.png | Adds new screenshot artifact for error state (dark/black). |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_empty_light_white.png | Adds new screenshot artifact for empty state (light/white). |
| app/screenshots/generic/debug/com.owncloud.android.ui.fragment.notifications.NotificationsFragmentIT_empty_light_black.png | Adds new screenshot artifact for empty state (light/black). |
Comments suppressed due to low confidence (1)
app/src/main/java/com/nextcloud/client/jobs/NotificationWork.kt:147
- When
file == nullthe pending intent targetsNavigatorActivitybut does not includeNavigatorActivity.EXTRA_SCREEN.NavigatorActivityrequires that extra and will currently return early / crash later. Build the intent viaNavigatorActivity.intent(context, NavigatorScreen.Notifications)(and keep addingKEY_NOTIFICATION_ACCOUNT+ flags) so tapping the push notification reliably opens the notifications screen.
val intent: Intent
if (file == null) {
intent = Intent(context, NavigatorActivity::class.java)
} else {
intent = Intent(context, FileDisplayActivity::class.java)
intent.action = Intent.ACTION_VIEW
intent.putExtra(FileDisplayActivity.KEY_FILE_ID, file.id)
}
intent.putExtra(KEY_NOTIFICATION_ACCOUNT, user.accountName)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| binding = ActivityNavigatorBinding.inflate(layoutInflater) | ||
| setContentView(R.layout.activity_navigator) | ||
|
|
||
| val screen = intent.getParcelableArgument(EXTRA_SCREEN, NavigatorScreen::class.java) ?: return | ||
| val fragmentContainerView = findViewById<FragmentContainerView>(R.id.fragment_container_view) | ||
| navigator = Navigator(supportFragmentManager, fragmentContainerView) | ||
| navigator = Navigator(supportFragmentManager, binding.fragmentContainerView) |
There was a problem hiding this comment.
binding is inflated but the activity content view is set via setContentView(R.layout.activity_navigator), so binding.fragmentContainerView / binding.drawerLayout are not the views actually shown on screen. This breaks navigation (transactions target a container not attached to the window) and drawer locking. Use setContentView(binding.root) (or drop the binding inflation entirely) and ensure you don't keep a detached binding around.
| setContentView(R.layout.activity_navigator) | ||
|
|
||
| val screen = intent.getParcelableArgument(EXTRA_SCREEN, NavigatorScreen::class.java) ?: return | ||
| val fragmentContainerView = findViewById<FragmentContainerView>(R.id.fragment_container_view) | ||
| navigator = Navigator(supportFragmentManager, fragmentContainerView) | ||
| navigator = Navigator(supportFragmentManager, binding.fragmentContainerView) | ||
| setupBackPressedHandler() |
There was a problem hiding this comment.
If EXTRA_SCREEN is missing, onCreate returns early without initializing navigator, leaving the activity in a partially constructed state (later lifecycle methods will crash on navigator access). Consider calling finish() (or throwing) when the screen extra is absent, and avoid continuing with an unusable activity instance.
|
|
||
| // region Button binding | ||
| fun bindButtons(holder: NotificationViewHolder, notification: Notification) { | ||
| holder.binding.dismiss.setOnClickListener { |
There was a problem hiding this comment.
Dismiss currently triggers deleteNotification(notification.notificationId) but no UI removal happens: NotificationsFragment.deleteNotification() only calls onRemovedNotification() which is a no-op on success, and the adapter never passes a ViewHolder/position to remove. Result: the notification stays visible after successful delete. Consider passing the holder (or adapter position / notification id) into the delete callback so you can optimistically remove via removeNotification(holder) and restore/refetch on failure.
| holder.binding.dismiss.setOnClickListener { | |
| holder.binding.dismiss.setOnClickListener { | |
| removeNotification(holder) |
| // region Adapter overrides | ||
|
|
||
| override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = NotificationViewHolder( | ||
| NotificationListItemBinding.inflate(LayoutInflater.from(fragment.requireContext())) |
There was a problem hiding this comment.
NotificationListItemBinding.inflate(LayoutInflater.from(...)) is called without the parent ViewGroup, so the item view won't receive the correct LayoutParams from the RecyclerView and can render/measure incorrectly. Inflate with the parent and attachToParent = false to ensure proper layout params.
| NotificationListItemBinding.inflate(LayoutInflater.from(fragment.requireContext())) | |
| NotificationListItemBinding.inflate(LayoutInflater.from(fragment.requireContext()), parent, false) |
| private var state: NotificationsUIState = NotificationsUIState.Loading | ||
| set(value) { | ||
| field = value | ||
| renderState(value) | ||
| } | ||
|
|
||
| // region Lifecycle | ||
| override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { | ||
| binding = NotificationsLayoutBinding.inflate(inflater, container, false) | ||
| return binding!!.root | ||
| } | ||
|
|
||
| override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| super.onViewCreated(view, savedInstanceState) | ||
| val initForTesting = activity?.intent?.getBooleanExtra(EXTRA_INIT_FOR_TESTING, false) | ||
| if (initForTesting == true) return | ||
|
|
There was a problem hiding this comment.
state is initialized to Loading, but Kotlin property initialization does not invoke the custom setter, so renderState(Loading) is never called initially. Combined with both swipe layouts being visible in XML, the first frame can show overlapping content until the first state change. Trigger an initial renderState(state) after inflating the binding (or set initial visibilities in XML to match Loading).
| private fun setupPushWarning() { | ||
| if (!resources.getBoolean(R.bool.show_push_warning)) return | ||
|
|
||
| if (snackbar?.isShown == false) { | ||
| snackbar?.show() | ||
| return | ||
| } | ||
|
|
There was a problem hiding this comment.
setupPushWarning() only returns early when snackbar?.isShown == false; if the snackbar is already created and currently shown, the method continues and may create/show another snackbar instance. Consider handling the snackbar != null case explicitly (e.g., if shown -> return, else show+return) to avoid duplicate snackbars.
| <FrameLayout | ||
| xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| xmlns:tools="http://schemas.android.com/tools" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent"> | ||
| android:layout_height="match_parent" | ||
| android:layout_below="@id/appbar" | ||
| app:layout_behavior="@string/appbar_scrolling_view_behavior"> |
There was a problem hiding this comment.
android:layout_below and app:layout_behavior on this root FrameLayout won't be applied as expected because the fragment root is hosted inside a FragmentContainerView (not a RelativeLayout/CoordinatorLayout). Keeping these attributes is misleading and may trigger lint warnings; consider removing them and rely on the container view's layout params/behavior instead (see activity_navigator.xml where the FragmentContainerView already has the scrolling behavior).
Changes
NavigatorActivityfor non-drawer type screenNotificationsFragment