Skip to content

feat: opt-in sync of deletes and restores from web to Android #16732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

aleksandrsovtan
Copy link
Contributor

Description

Functionality for synchronizing the local media store when remotely deleting to trash and restoring files has been added. (Android)
This solves the issue of file deletion synchronization on web and mobile apps.

Fixes # (issue)

How Has This Been Tested?

To verify this, you need an Android 11+ device.

  1. Launch the Flutter app on an Android device.
  2. Log in to the app (I used the demo server and demo account).
  3. Go to Settings => Advanced and enable the 'Sync remote deletions' switch.
  4. Download any photo or multiple photos from the photo list.
  5. Grant access to local albums, specifically to the Immich album (Cloud icon to the left of the profile icon).
  6. Open the Immich web app and log in with the same account (I used the demo).
  7. In the web app, delete (move to trash) a photo that is locally downloaded on your phone (marked with a cloud + icon).
  8. Open the gallery, go to the trash, and you should see the file that is in the trash on the web app.
  9. Return to the mobile app.
  10. In the web app, restore the file from the trash, and make sure it appears in the Flutter app's photo list (with the cloud + icon).
  11. Open the gallery, and the file should be in the Immich folder (restored).

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if applicable
  • I have no unrelated changes in the PR.
  • I have confirmed that any new dependencies are strictly necessary.
  • I have written tests for new code (if applicable)
  • I have followed naming conventions/patterns in the surrounding code
  • All code in src/services/ uses repositories implementations for database calls, filesystem operations, etc.
  • All code in src/repositories/ is pretty basic/simple and does not have any immich specific logic (that belongs in src/services/)

@benmccann benmccann changed the title Features: Local file movement to trash and restoration back to the album added. (Android) feat: opt-in sync of deletes and restored from web to Android Mar 8, 2025
@benmccann benmccann changed the title feat: opt-in sync of deletes and restored from web to Android feat: opt-in sync of deletes and restores from web to Android Mar 8, 2025
SettingsSwitchListTile(
enabled: true,
valueNotifier: manageLocalMediaAndroid,
title: "Sync remote deletions",
Copy link
Contributor

Choose a reason for hiding this comment

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

Please put the string to en-us.json

suggestion for the subtitle string

Automatically delete or restore an asset on this device when that action is taken on the web

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I would like to mark this as [EXPERIMENTAL]

@alextran1502
Copy link
Contributor

Hello, I just ran the first round of functional testing on a Samsung S23, and I don't see the behavior I expected. Let me know if I misunderstood the PR's functionality.

  1. Make sure that the option is enabled
  2. Make sure the target asset is synced and present on the local device, meaning cloud + check icon.
  3. Go to the web and trash the asset, confirm that the asset is in the trash
  4. In the mobile app, I can see that the asset is in Immich's trash
  5. I checked the native file explorer; in this case, it is the Samsung "My File" app. I checked the trash page, but I don't see any trash file there.

Let me know if I have tested the PR correctly.

@@ -45,6 +47,13 @@ class AdvancedSettings extends HookConsumerWidget {
title: "advanced_settings_troubleshooting_title".tr(),
subtitle: "advanced_settings_troubleshooting_subtitle".tr(),
),
SettingsSwitchListTile(
Copy link
Contributor

Choose a reason for hiding this comment

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

This should only show on Android

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You tested it a bit incorrectly. The Media Store API works with media files that are located in the gallery. So, after deletion, we need to check the trash in the gallery, not the trash in the file manager.

When deleting a downloaded file in the web app, the file moves from the album to the trash (gallery), and when restoring it in the web app, it returns to the album from the trash.

Copy link
Member

@shenlong-tanwen shenlong-tanwen left a comment

Choose a reason for hiding this comment

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

I'm not sure it is a good idea to try to keep the Trash inside Immich to be in sync with the one on the device. We can configure the number of days after which an asset is permanently deleted from the trash in Immich. However, the System trash on the device defaults to a 30 days window, after which the asset is removed permanently from the device. If the trash days is >30 inside Immich and the user tries to restore the asset after 30 days, there will be no asset, anymore on the device to be restored. It is not a problem with the other app because it defaults to having 30 days as the trash window.

@benmccann
Copy link
Collaborator

I'm not sure it is a good idea to try to keep the Trash inside Immich to be in sync with the one on the device. We can configure the number of days after which an asset is permanently deleted from the trash in Immich. However, the System trash on the device defaults to a 30 days window, after which the asset is removed permanently from the device.

This is a good catch. I do think it's kind of nice that it's viewable in the system trash though. I think there are a few options here:

  • Immich does not use system trash and has its own trash
  • Immich removes ability to configure trash length and uses 30 days like Android and iOS
  • The photo is deleted on the mobile device at 30 days or Immich configured date - whichever is soonest. The image would probably be restorable from the server if it has a longer retention period
  • Same as the last option, but also cap the Immich retention at 30 days so the values do not conflict

Do you have an opinion @alextran1502 ?

@alextran1502
Copy link
Contributor

Immich removes ability to configure trash length and uses 30 days like Android and iOS

I think this is the best compromise. The default is always to trash and have 30 days before deletion.

@jrasm91 any thoughts about this?

@jrasm91
Copy link
Contributor

jrasm91 commented Mar 14, 2025

Personally, I think if you trash a file in Immich with this option enabled:

  • The web should show a message similar to google photos
  • The mobile app moves it to system trash
  • The server deletes it after the configured trash days
  • The mobile operating system deletes it following the system trash policy

Specifically when the Immich's trash policy in longer than 30 days I think it's fine for the item to be deleted on the device and only be restored on the web.

When the Immich trash policy is shorter than 30 days, I think it's also fine to leave the trashed photo in the mobile trash.

image

@benmccann
Copy link
Collaborator

I think that's probably pretty close to what happens with this PR today. I guess my question would be what happens if an asset is deleted from one trash and then restored from the other? Will it get synced to the other device? How and when does that happen? I think what Alex suggested is simpler conceptually in that it avoids cases like that.

It's been hard to keep this PR in sync with main and it keeps falling down the queue, so I sort of wonder if we shouldn't just merge it as is and figure out any follow ups separately.

@aleksandrsovtan
Copy link
Contributor Author

Hi @aleksandrsovtan, Thanks for the fix; I can now see the behavior in action! Great work

One change I would like to see is when the user toggles the feature; we request the file access permission right away. Then, only enable the feature if the permission is granted. This way, we can avoid weirdness happening in the app when the delete action is performed on the web and the app uses this mechanism for the first time.

Hi @alextran1502, done

@alextran1502
Copy link
Contributor

@aleksandrsovtan I just tested the permission logic and saw the following behaviors

  • Upon toggling the feature from the Immich app, the native permission prompt is presented as expected; however, when the user does not gain file access permission and returns to the app, the feature is still being turned on. I expect that if the user denies the permission, the feature will stay off.
  • If the file access permission is gained, toggling the feature should not show the native permission prompt again. The native prompt is shown each time the feature switches from off -> on.

Can you help me resolve those two issues? We are very close now! 😃

Comment on lines 31 to 34
when (call.method) {
"moveToTrash" -> {
val fileName = call.argument<String>("fileName")
if (fileName != null) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you re-use the existing method channel in BackgroundServicePlugin?

Copy link
Member

Choose a reason for hiding this comment

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

@aleksandrsovtan Can you resolve this comment to proceed with the review?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @aleksandrsovtan, Can you help resolve this comment, moving the code to BackgroundServicePlugin so that we have better separation? I think after this resolved, we can emerge the PR. I just tested again and everything looks good

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This might be a challenge, since the background service plugin doesn't have an activity. I'll try to do it ASAP

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aleksandrsovtan
Copy link
Contributor Author

@aleksandrsovtan I just tested the permission logic and saw the following behaviors

  • Upon toggling the feature from the Immich app, the native permission prompt is presented as expected; however, when the user does not gain file access permission and returns to the app, the feature is still being turned on. I expect that if the user denies the permission, the feature will stay off.
  • If the file access permission is gained, toggling the feature should not show the native permission prompt again. The native prompt is shown each time the feature switches from off -> on.

Can you help me resolve those two issues? We are very close now! 😃

@alextran1502 done!)

@alextran1502 alextran1502 merged commit 2b131fe into immich-app:main Apr 8, 2025
39 checks passed
alextran1502 added a commit that referenced this pull request Apr 23, 2025
alextran1502 added a commit that referenced this pull request Apr 23, 2025
* chore: revert #16732

* lint
@BWagener
Copy link

I really hope there is a solution for the permission problem with the playstore.

Is there already an issue for this? Or is this the place to discuss it?

@shenlong-tanwen
Copy link
Member

I really hope there is a solution for the permission problem with the playstore.

We are in the process of adding it back using a less intrusive permission on Android - #17828.

@cchristchurch
Copy link

I have updated both the android app and the server to 1.132.3, however I still cannot see the experimental setting "Sync remote deletions" in the "Advanced" settings section. What am I missing?

Note that my phone is still running Android 11, could that be the reason?

savely-krasovsky pushed a commit to savely-krasovsky/immich that referenced this pull request Jun 8, 2025
…-app#16732)

* Features: Local file movement to trash and restoration back to the album added. (Android)

* Comments fixes

* settings button marked as [EXPERIMENTAL]

* _moveToTrashMatchedAssets refactored, moveToTrash renamed.

* fix: bad merge

* Permission check and request for local storage added.

* Permission request added on settings switcher

* Settings button logic changed

* Method channel file_trash moved to BackgroundServicePlugin

---------

Co-authored-by: Alex <[email protected]>
savely-krasovsky pushed a commit to savely-krasovsky/immich that referenced this pull request Jun 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants