Skip to content

Fix #477: "Device" contacts get deleted by account changes from across the system - #497

Open
Adam-Winwood wants to merge 2 commits into
you-apps:mainfrom
Adam-Winwood:fix/477-device-contacts-account
Open

Fix #477: "Device" contacts get deleted by account changes from across the system#497
Adam-Winwood wants to merge 2 commits into
you-apps:mainfrom
Adam-Winwood:fix/477-device-contacts-account

Conversation

@Adam-Winwood

Copy link
Copy Markdown

The issue

Issue #477 and #275 both have contacts disappearing after some amount of delay. This could readily be explained by the following: contacts made under the DEVICE account are saved using the dummy account values ("DEVICE", "com.android.contacts"); whenever an account is added or removed on the device, Android cleans up all contacts which have invalid (non-existing) accounts. So all contacts made in the app under DEVICE will be deleted.

The fix

The fix comes in two commits:

  1. change to representation of accounts in live memory - handles new DEVICE accounts
  2. migration of all pre-existing contacts to the new format

Tested on emulator and my actual Samsung device (though the migration is a no-op on the Samsung).

Known limitations

At least one version of Samsung's Android has a bug where trying to set account_type and account_name to null just throws an exception. In this case the migration gives up and doesn't run again.

Either we accept that the issue is fixed for all new contacts Samsung users create and we abandon the pre-existing ones to deletion, or we write some code to delete and then re-create the relevant contacts which is a more aggressive operation. I don't feel comfortable making the call on that.

Another concern is that, if there can be a bug in one OEM version of Android, then there could be another. Perhaps we should wrap the whole function in a catch all to prevent a repeated crash-on-startup?

Notes

This attempt at fixing the issue used no AI.

Adam W added 2 commits July 30, 2026 14:19
Warning
-------

This doesn't fix you-apps#477 for previously existing contacts - it only
changes how they are handled in the live app representation.

Context
-------

Previous to this commit, contacts were saved through the
ContactsProvider with accountName and accountType set to fake account
values ("Device" and "com.android.contacts" resp.).  Unfortunately,
Android occasionally deletes contacts that have an account that doesn't
currently exist on this device - this includes all contacts we make
with the fake account values.

Intent
------

In this commit we instead write out (null, null) through
ContactsProvider.  This is the way Android intends you to represent
the no-account case.

Expand AccountType's conceptual scope to represent the whole idea of an
account within the app.  This explicitly recognises the special case
within the data model, makes certain invalid states unrepresentable,
makes it easier for the compiler to check exhaustiveness of related
code, and centralises serialization (to/from for prefs/contacts).

Changes
-------

Rewrite AccountType as a sealed interface
- normal case -> RealAccountType
- device special case -> DeviceAccountType
- handles to and from storage in preferences
- handles to and from ContactsProvider account columns

Use AccountType directly in ContactData rather than separate
accountName and accountType fields.
Warning
-------

It shouldn't throw any exceptions, however I can't guarantee all OEM
versions of Android are safe - for instance Samsung has a bug which
throws IllegalArgumentException.  If there are any **other** issues like
this, we will soon find out about it as that would mean a crash on app
startup!

Details
-------

This commit migrates pre-existing contacts from the legacy DEVICE
account values ("Device", "com.android.contacts") to null.  This is
Android's intended way to store contacts that aren't owned by a
sync-ing account.

This completes the fix of you-apps#477 that the previous commit began.

Implemented as a function fired from app startup, gated behind a
preference value so it never fires again.

Note that we guard against the possibility of a real account having
these values and retry later if so.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant