Skip to content

feat(navigator): notifications#16800

Open
alperozturk96 wants to merge 16 commits intomasterfrom
feat/navigator-notifications
Open

feat(navigator): notifications#16800
alperozturk96 wants to merge 16 commits intomasterfrom
feat/navigator-notifications

Conversation

@alperozturk96
Copy link
Copy Markdown
Collaborator

@alperozturk96 alperozturk96 commented Apr 2, 2026

Changes

  • Makes compatible NavigatorActivity for non-drawer type screen
  • Adds NotificationsFragment
  • Converts Java classes to Kotlin
  • Replaces deprecated async-tasks with Coroutines
  • Simplify logics

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23901488585/artifacts/6242414452
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23903317034/artifacts/6242813250
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@alperozturk96 alperozturk96 added the performance 🚀 Performance improvement opportunities (non-crash related) label Apr 2, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23903829241/artifacts/6243019116
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23904463648/artifacts/6243298905
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@alperozturk96 alperozturk96 changed the title fix(navigator): notifications feat(navigator): notifications Apr 2, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23913648608/artifacts/6247279485
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23914107506/artifacts/6247475169
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

APK file: https://github.com/nextcloud/android/actions/runs/23915475261/artifacts/6248206586
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@alperozturk96 alperozturk96 force-pushed the feat/navigator-notifications branch from 11a0d1c to 216d445 Compare April 8, 2026 07:11
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

APK file: https://github.com/nextcloud/android/actions/runs/24123009774/artifacts/6322344961
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

APK file: https://github.com/nextcloud/android/actions/runs/24123396708/artifacts/6322495416
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@alperozturk96 alperozturk96 force-pushed the feat/navigator-notifications branch from b7c5699 to 7ae80ba Compare April 8, 2026 10:08
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

APK file: https://github.com/nextcloud/android/actions/runs/24129902593/artifacts/6325147062
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@alperozturk96 alperozturk96 force-pushed the feat/navigator-notifications branch from 7ae80ba to c801165 Compare April 9, 2026 13:41
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

APK file: https://github.com/nextcloud/android/actions/runs/24193410737/artifacts/6351393880
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

APK file: https://github.com/nextcloud/android/actions/runs/24194734050/artifacts/6351933965
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

APK file: https://github.com/nextcloud/android/actions/runs/24238133672/artifacts/6368961516
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

APK file: https://github.com/nextcloud/android/actions/runs/24239954726/artifacts/6369704928
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

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>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
@github-actions
Copy link
Copy Markdown

APK file: https://github.com/nextcloud/android/actions/runs/24249789666/artifacts/6373869333
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

Codacy

SpotBugs

CategoryBaseNew
Bad practice3635
Correctness6969
Dodgy code228226
Experimental11
Internationalization77
Malicious code vulnerability22
Multithreaded correctness3434
Performance4242
Security1616
Total435432

@github-actions
Copy link
Copy Markdown

blue-Light-Screenshot test failed, but no output was generated. Maybe a preliminary stage failed.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.Notifications and a new NotificationsFragment UI/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 == null the pending intent targets NavigatorActivity but does not include NavigatorActivity.EXTRA_SCREEN. NavigatorActivity requires that extra and will currently return early / crash later. Build the intent via NavigatorActivity.intent(context, NavigatorScreen.Notifications) (and keep adding KEY_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.

Comment on lines 29 to +35
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)
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 32 to 36
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()
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.

// region Button binding
fun bindButtons(holder: NotificationViewHolder, notification: Notification) {
holder.binding.dismiss.setOnClickListener {
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
holder.binding.dismiss.setOnClickListener {
holder.binding.dismiss.setOnClickListener {
removeNotification(holder)

Copilot uses AI. Check for mistakes.
// region Adapter overrides

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = NotificationViewHolder(
NotificationListItemBinding.inflate(LayoutInflater.from(fragment.requireContext()))
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
NotificationListItemBinding.inflate(LayoutInflater.from(fragment.requireContext()))
NotificationListItemBinding.inflate(LayoutInflater.from(fragment.requireContext()), parent, false)

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +100
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

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +245 to +252
private fun setupPushWarning() {
if (!resources.getBoolean(R.bool.show_push_warning)) return

if (snackbar?.isShown == false) {
snackbar?.show()
return
}

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +16
<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">
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review performance 🚀 Performance improvement opportunities (non-crash related)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants