diff --git a/app/src/main/java/foundation/e/notes/migration/Migration_24_25.kt b/app/src/main/java/foundation/e/notes/migration/Migration_24_25.kt
new file mode 100644
index 0000000000000000000000000000000000000000..86b40c6c458b58df6793cbbd64007045464056d0
--- /dev/null
+++ b/app/src/main/java/foundation/e/notes/migration/Migration_24_25.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright e Foundation 2025
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.notes.migration
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+/**
+ * Deletes accounts without any associated notes.
+ * */
+class Migration_24_25 : Migration(24, 25) {
+ override fun migrate(db: SupportSQLiteDatabase) {
+ db.execSQL(
+ """
+ DELETE FROM Account WHERE id NOT IN (SELECT DISTINCT accountId FROM Note);
+ """.trimIndent()
+ )
+ }
+}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java
index 0ab3fd9bf5c4da89dc08d5fe52dfee0cdb9f0c38..a25143c87608e1fa6449b0243c63d5f4f1fff065 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountActivity.java
@@ -19,6 +19,7 @@ import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedExcepti
import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException;
import com.nextcloud.android.sso.exceptions.UnknownErrorException;
import com.nextcloud.android.sso.helper.SingleAccountHelper;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
import com.nextcloud.android.sso.ui.UiExceptionManager;
import java.net.HttpURLConnection;
@@ -120,8 +121,13 @@ public class ImportAccountActivity extends AppCompatActivity {
AccountImporter.onActivityResult(requestCode, resultCode, data, ImportAccountActivity.this, ssoAccount -> {
runOnUiThread(() -> binding.progressCircular.setVisibility(View.VISIBLE));
- SingleAccountHelper.setCurrentAccount(getApplicationContext(), ssoAccount.name);
executor.submit(() -> {
+ if (isExistingAccount(ssoAccount)) {
+ finishActivity(false);
+ return;
+ }
+
+ SingleAccountHelper.setCurrentAccount(getApplicationContext(), ssoAccount.name);
Log.i(TAG, "Added account: " + "name:" + ssoAccount.name + ", " + ssoAccount.url + ", userId" + ssoAccount.userId);
try {
Log.i(TAG, "Loading capabilities for " + ssoAccount.name);
@@ -192,6 +198,10 @@ public class ImportAccountActivity extends AppCompatActivity {
}
}
+ private boolean isExistingAccount(SingleSignOnAccount ssoAccount) {
+ return importAccountViewModel.isExistingAccount(ssoAccount);
+ }
+
private void finishActivity(boolean isLocal) {
setResult(RESULT_OK);
Intent intent = new Intent(ImportAccountActivity.this, MainActivity.class);
@@ -243,7 +253,7 @@ public class ImportAccountActivity extends AppCompatActivity {
private void handleLocalAccount() {
binding.addLocalButton.setOnClickListener(view -> {
- localAccountViewModel.logInToLocalAccount(getAddLocalAccountCallback());
+ localAccountViewModel.logInToLocalAccount(getApplicationContext(), getAddLocalAccountCallback());
});
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountViewModel.java
index 1d60a0434ca61c42b2ce1881b7b078139f8451e8..4affddb0a517dac22f6d1d6faaa4e24f463f68d8 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountViewModel.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/importaccount/ImportAccountViewModel.java
@@ -7,6 +7,8 @@ import androidx.annotation.Nullable;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
+
import it.niedermann.owncloud.notes.persistence.NotesRepository;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.shared.model.Capabilities;
@@ -26,4 +28,14 @@ public class ImportAccountViewModel extends AndroidViewModel {
public LiveData addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback callback) {
return repo.addAccount(url, username, accountName, capabilities, displayName, callback);
}
+
+ protected boolean isExistingAccount(@NonNull SingleSignOnAccount ssoAccount) {
+ Account account = repo.getAccountByName(ssoAccount.name);
+
+ if (account == null) {
+ return false;
+ }
+
+ return account.getUrl().equals(ssoAccount.url);
+ }
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java
index ccad6f2c67bb4fddacfb2dae5efd98657493eb02..187796ab7126065c31087260222788295e8341f2 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java
@@ -823,7 +823,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onAddLocalAccountButtonClick() {
- localAccountViewModel.logInToLocalAccount(new IResponseCallback<>() {
+ localAccountViewModel.logInToLocalAccount(getApplicationContext(), new IResponseCallback<>() {
@Override
public void onSuccess(@NonNull LocalAccountBundle result) {
runOnUiThread(() -> {
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java
index 3072aa1bd3dd5defa80528711307bc3ab325de24..e9f2128a24dc16cb08426bb80eae061234250c05 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesDatabase.java
@@ -12,6 +12,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase;
import java.util.List;
import foundation.e.notes.migration.Migration_23_24;
+import foundation.e.notes.migration.Migration_24_25;
import it.niedermann.owncloud.notes.persistence.dao.AccountDao;
import it.niedermann.owncloud.notes.persistence.dao.CategoryOptionsDao;
import it.niedermann.owncloud.notes.persistence.dao.NoteDao;
@@ -46,7 +47,7 @@ import trikita.log.Log;
CategoryOptions.class,
SingleNoteWidgetData.class,
NotesListWidgetData.class
- }, version = 24
+ }, version = 25
)
@TypeConverters({Converters.class})
public abstract class NotesDatabase extends RoomDatabase {
@@ -82,7 +83,8 @@ public abstract class NotesDatabase extends RoomDatabase {
new Migration_20_21(),
new Migration_21_22(context),
new Migration_22_23(),
- new Migration_23_24()
+ new Migration_23_24(),
+ new Migration_24_25()
)
.fallbackToDestructiveMigrationOnDowngrade()
.fallbackToDestructiveMigration()
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/account/LocalAccountViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/account/LocalAccountViewModel.java
index f6bf22ecec367331b37550fdba4c58a0e6d326a5..aab31d05fb882f90d90f96f844663ce72c2c37a3 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/shared/account/LocalAccountViewModel.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/account/LocalAccountViewModel.java
@@ -17,11 +17,13 @@
package it.niedermann.owncloud.notes.shared.account;
import android.app.Application;
+import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import com.nextcloud.android.sso.AccountImporter;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
import java.util.concurrent.ExecutorService;
@@ -36,8 +38,8 @@ import trikita.log.Log;
public class LocalAccountViewModel extends AndroidViewModel {
+ private static final String ACCOUNT_NAME_LOCAL = "Local";
private static final String TAG = LocalAccountViewModel.class.getSimpleName();
-
@NonNull
private final NotesRepository notesRepository;
@@ -50,31 +52,72 @@ public class LocalAccountViewModel extends AndroidViewModel {
executor = Executors.newSingleThreadExecutor();
}
- public void logInToLocalAccount(@NonNull IResponseCallback callback) {
+ private static boolean isExistingLocalAccount(@NonNull SingleSignOnAccount ssoAccount) {
+ return !ssoAccount.name.isEmpty() && !ssoAccount.userId.isEmpty() && !ssoAccount.type.isEmpty();
+ }
+
+ public void logInToLocalAccount(Context context, @NonNull IResponseCallback callback) {
executor.submit(() -> {
- SingleSignOnAccount singleSignOnAccount = AccountImporter.pickLocalAccount(getApplication());
- Log.i(TAG, "Added local account: " + "name:" + singleSignOnAccount.name);
try {
- final var capabilities = new Capabilities();
- final String displayName = singleSignOnAccount.name;
- notesRepository.addAccount(singleSignOnAccount.url, singleSignOnAccount.userId, singleSignOnAccount.name, capabilities, displayName, new IResponseCallback<>() {
- @Override
- public void onSuccess(Account account) {
- final var bundle = new LocalAccountBundle(singleSignOnAccount, account, capabilities);
- callback.onSuccess(bundle);
- }
-
- @Override
- public void onError(@NonNull Throwable throwable) {
- Log.e(TAG, throwable);
- callback.onError(throwable);
- }
- });
- } catch (Throwable t) {
- ApiProvider.getInstance().invalidateAPICache(singleSignOnAccount);
- Log.e(TAG, t);
- callback.onError(t);
+ Account account = getExistingAccount();
+ if (account != null) {
+ handleExistingAccount(context, account, callback);
+ return;
+ }
+ } catch (NextcloudFilesAppAccountNotFoundException accountNotFoundException) {
+ Log.e(TAG, "Failed to get SingleSignOn account: " + accountNotFoundException);
}
+ addNewAccount(callback);
});
}
+
+ private Account getExistingAccount() {
+ return notesRepository.getAccountByName(ACCOUNT_NAME_LOCAL);
+ }
+
+ private void addNewAccount(@NonNull IResponseCallback callback) {
+ SingleSignOnAccount singleSignOnAccount = createSingleSignOnAccount();
+ try {
+ final var capabilities = new Capabilities();
+ final String displayName = singleSignOnAccount.name;
+
+ notesRepository.addAccount(singleSignOnAccount.url, singleSignOnAccount.userId,
+ singleSignOnAccount.name, capabilities, displayName, new IResponseCallback<>() {
+ @Override
+ public void onSuccess(Account account) {
+ final var bundle = new LocalAccountBundle(singleSignOnAccount, account, capabilities);
+ Log.d(TAG, "onSuccess: added local account, name: " + singleSignOnAccount.name);
+ callback.onSuccess(bundle);
+ }
+
+ @Override
+ public void onError(@NonNull Throwable throwable) {
+ Log.e(TAG, "onError: couldn't add local account, error: " + throwable);
+ callback.onError(throwable);
+ }
+ });
+ } catch (Throwable throwable) {
+ ApiProvider.getInstance().invalidateAPICache(singleSignOnAccount);
+ Log.e(TAG, throwable);
+ callback.onError(throwable);
+ }
+ }
+
+ private void handleExistingAccount(Context context, @NonNull Account account, IResponseCallback callback)
+ throws NextcloudFilesAppAccountNotFoundException {
+ final SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(context, ACCOUNT_NAME_LOCAL);
+ if (isExistingLocalAccount(ssoAccount)) {
+ Log.i(TAG, "Local account already exists, reusing it; account name: " + ssoAccount.name);
+ final Capabilities capabilities = new Capabilities();
+ final LocalAccountBundle bundle = new LocalAccountBundle(ssoAccount, account, capabilities);
+ callback.onSuccess(bundle);
+ }
+ }
+
+ @NonNull
+ private SingleSignOnAccount createSingleSignOnAccount() {
+ SingleSignOnAccount singleSignOnAccount = AccountImporter.pickLocalAccount(getApplication());
+ Log.i(TAG, "New local account created; account name: " + singleSignOnAccount.name);
+ return singleSignOnAccount;
+ }
}