Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-notes.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Niedermann <info@niedermann.it>2020-10-16 17:13:23 +0300
committerStefan Niedermann <info@niedermann.it>2020-10-16 17:13:23 +0300
commit8f941964ba63205b95eb5254f1ce0c8fcd318b77 (patch)
treed70069d623f3189313e1264b3fa6a51d41ef775b
parent3159c4dfdeddd386fb7af586b44c4fd03894550b (diff)
Fix branding
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/AppendToNoteActivity.java4
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java5
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java59
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/MainActivity.java146
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java150
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java139
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java35
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NotesListViewItemTouchHelper.java19
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java8
-rw-r--r--app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetConfigurationActivity.java2
-rw-r--r--app/src/main/res/layout/activity_notes_list_view.xml1
11 files changed, 351 insertions, 217 deletions
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/AppendToNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/AppendToNoteActivity.java
index f4bfc32b..66a70987 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/AppendToNoteActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/AppendToNoteActivity.java
@@ -36,7 +36,7 @@ public class AppendToNoteActivity extends MainActivity {
@Override
public void onNoteClick(int position, View v) {
if (receivedText != null && receivedText.length() > 0) {
- final NoteWithCategory note = db.getNoteDao().getNoteWithCategory(localAccount.getId(), ((NoteWithCategory) adapter.getItem(position)).getId());
+ final NoteWithCategory note = mainViewModel.getNoteWithCategory(localAccount.getId(), ((NoteWithCategory) adapter.getItem(position)).getId());
final String oldContent = note.getContent();
String newContent;
if (oldContent != null && oldContent.length() > 0) {
@@ -44,7 +44,7 @@ public class AppendToNoteActivity extends MainActivity {
} else {
newContent = receivedText;
}
- db.updateNoteAndSync(ssoAccount, localAccount, note, newContent, null, () -> Toast.makeText(this, getString(R.string.added_content, receivedText), Toast.LENGTH_SHORT).show());
+ mainViewModel.updateNoteAndSync(ssoAccount, localAccount, note, newContent, null, () -> Toast.makeText(this, getString(R.string.added_content, receivedText), Toast.LENGTH_SHORT).show());
} else {
Toast.makeText(this, R.string.shared_text_empty, Toast.LENGTH_SHORT).show();
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java
index a9d88c32..70b07af0 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedActivity.java
@@ -17,6 +17,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import it.niedermann.owncloud.notes.R;
+import static it.niedermann.owncloud.notes.branding.BrandingUtil.readBrandColors;
import static it.niedermann.owncloud.notes.branding.BrandingUtil.tintMenuIcon;
public abstract class BrandedActivity extends AppCompatActivity implements Branded {
@@ -38,9 +39,7 @@ public abstract class BrandedActivity extends AppCompatActivity implements Brand
colorAccent = typedValue.data;
if (BrandingUtil.isBrandingEnabled(this)) {
- @ColorInt final int mainColor = BrandingUtil.readBrandMainColor(this);
- @ColorInt final int textColor = BrandingUtil.readBrandTextColor(this);
- applyBrand(mainColor, textColor);
+ readBrandColors(this).observe(this, (pair) -> applyBrand(pair.first, pair.second));
}
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java
index 3b3ea7f2..661b956d 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandingUtil.java
@@ -14,8 +14,13 @@ import androidx.annotation.ColorInt;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.DrawableCompat;
+import androidx.core.util.Pair;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
+import androidx.lifecycle.MutableLiveData;
import androidx.preference.PreferenceManager;
+import it.niedermann.android.sharedpreferences.SharedPreferenceIntLiveData;
import it.niedermann.owncloud.notes.NotesApplication;
import it.niedermann.owncloud.notes.R;
@@ -36,6 +41,52 @@ public class BrandingUtil {
return prefs.getBoolean(context.getString(R.string.pref_key_branding), true);
}
+ public static LiveData<Pair<Integer, Integer>> readBrandColors(@NonNull Context context) {
+ return new BrandingLiveData(context);
+ }
+
+ private static class BrandingLiveData extends MediatorLiveData<Pair<Integer, Integer>> {
+ @ColorInt
+ Integer lastMainColor = null;
+ @ColorInt
+ Integer lastTextColor = null;
+
+ public BrandingLiveData(@NonNull Context context) {
+ addSource(readBrandMainColorLiveData(context), (nextMainColor) -> {
+ lastMainColor = nextMainColor;
+ if (lastTextColor != null) {
+ postValue(new Pair<>(lastMainColor, lastTextColor));
+ }
+ });
+ addSource(readBrandTextColorLiveData(context), (nextTextColor) -> {
+ lastTextColor = nextTextColor;
+ if (lastMainColor != null) {
+ postValue(new Pair<>(lastMainColor, lastTextColor));
+ }
+ });
+ }
+ }
+
+ public static LiveData<Integer> readBrandMainColorLiveData(@NonNull Context context) {
+ if (BrandingUtil.isBrandingEnabled(context)) {
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+ Log.v(TAG, "--- Read: shared_preference_theme_main");
+ return new SharedPreferenceIntLiveData(sharedPreferences, pref_key_branding_main, context.getApplicationContext().getResources().getColor(R.color.defaultBrand));
+ } else {
+ return new MutableLiveData<>(context.getResources().getColor(R.color.defaultBrand));
+ }
+ }
+
+ public static LiveData<Integer> readBrandTextColorLiveData(@NonNull Context context) {
+ if (BrandingUtil.isBrandingEnabled(context)) {
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+ Log.v(TAG, "--- Read: shared_preference_theme_text");
+ return new SharedPreferenceIntLiveData(sharedPreferences, pref_key_branding_text, Color.WHITE);
+ } else {
+ return new MutableLiveData<>(Color.WHITE);
+ }
+ }
+
@ColorInt
public static int readBrandMainColor(@NonNull Context context) {
if (BrandingUtil.isBrandingEnabled(context)) {
@@ -59,20 +110,12 @@ public class BrandingUtil {
}
public static void saveBrandColors(@NonNull Context context, @ColorInt int mainColor, @ColorInt int textColor) {
- final int previousMainColor = readBrandMainColor(context);
- final int previousTextColor = readBrandTextColor(context);
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
Log.v(TAG, "--- Write: shared_preference_theme_main" + " | " + mainColor);
Log.v(TAG, "--- Write: shared_preference_theme_text" + " | " + textColor);
editor.putInt(pref_key_branding_main, mainColor);
editor.putInt(pref_key_branding_text, textColor);
editor.apply();
- if (isBrandingEnabled(context) && context instanceof BrandedActivity) {
- if (mainColor != previousMainColor || textColor != previousTextColor) {
- final BrandedActivity activity = (BrandedActivity) context;
- activity.runOnUiThread(activity::recreate);
- }
- }
}
/**
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 2e86d64f..49f796ef 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
@@ -26,6 +26,7 @@ import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -39,14 +40,11 @@ import com.google.android.material.snackbar.Snackbar;
import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.exceptions.AccountImportCancelledException;
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
-import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException;
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
import com.nextcloud.android.sso.exceptions.TokenMismatchException;
import com.nextcloud.android.sso.helper.SingleAccountHelper;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
-import java.net.HttpURLConnection;
-import java.util.ArrayList;
import java.util.List;
import it.niedermann.owncloud.notes.LockedActivity;
@@ -55,7 +53,6 @@ import it.niedermann.owncloud.notes.accountpicker.AccountPickerListener;
import it.niedermann.owncloud.notes.accountswitcher.AccountSwitcherDialog;
import it.niedermann.owncloud.notes.accountswitcher.AccountSwitcherListener;
import it.niedermann.owncloud.notes.branding.BrandedSnackbar;
-import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.databinding.ActivityNotesListViewBinding;
import it.niedermann.owncloud.notes.databinding.DrawerLayoutBinding;
import it.niedermann.owncloud.notes.edit.EditNoteActivity;
@@ -71,8 +68,6 @@ import it.niedermann.owncloud.notes.main.navigation.NavigationClickListener;
import it.niedermann.owncloud.notes.main.navigation.NavigationItem;
import it.niedermann.owncloud.notes.persistence.CapabilitiesClient;
import it.niedermann.owncloud.notes.persistence.CapabilitiesWorker;
-import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper;
-import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Category;
import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory;
@@ -103,8 +98,6 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
protected MainViewModel mainViewModel;
- protected NotesDatabase db = null;
-
private boolean gridView = true;
public static final String CREATED_NOTE = "it.niedermann.owncloud.notes.created_notes";
@@ -112,16 +105,14 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
public static final String ADAPTER_KEY_STARRED = "starred";
public static final String ADAPTER_KEY_UNCATEGORIZED = "uncategorized";
- protected ItemAdapter adapter;
-
private final static int create_note_cmd = 0;
private final static int show_single_note_cmd = 1;
public final static int manage_account = 4;
- /**
- * Used to detect the onResume() call after the import dialog has been displayed.
- * https://github.com/stefan-niedermann/nextcloud-notes/pull/599/commits/f40eab402d122f113020200751894fa39c8b9fcc#r334239634
- */
+ protected ItemAdapter adapter;
+ private NavigationAdapter adapterCategories;
+ private MenuAdapter menuAdapter;
+
private boolean notAuthorizedAccountHandled = false;
protected SingleSignOnAccount ssoAccount;
@@ -129,16 +120,13 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
protected DrawerLayoutBinding binding;
protected ActivityNotesListViewBinding activityBinding;
-
+ protected FloatingActionButton fabCreate;
private CoordinatorLayout coordinatorLayout;
private SwipeRefreshLayout swipeRefreshLayout;
- protected FloatingActionButton fabCreate;
private RecyclerView listView;
+ private ActionMode mActionMode;
- private NavigationAdapter adapterCategories;
- private MenuAdapter menuAdapter;
boolean canMoveNoteToAnotherAccounts = false;
- private ActionMode mActionMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -156,9 +144,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
this.fabCreate = binding.activityNotesListView.fabCreate;
this.listView = binding.activityNotesListView.recyclerView;
- db = NotesDatabase.getInstance(this);
-
gridView = isGridViewEnabled();
+
if (!gridView || isDarkThemeActive(this)) {
activityBinding.activityNotesListView.setBackgroundColor(ContextCompat.getColor(this, R.color.primary));
}
@@ -167,6 +154,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
setupNavigationList();
setupNotesList();
+ mainViewModel.hasMultipleAccountsConfigured().observe(this, (hasMultipleAccountsConfigured) -> canMoveNoteToAnotherAccounts = hasMultipleAccountsConfigured);
mainViewModel.getSyncStatus().observe(this, (syncStatus) -> swipeRefreshLayout.setRefreshing(syncStatus));
mainViewModel.getSyncErrors().observe(this, (exceptions) -> BrandedSnackbar.make(coordinatorLayout, R.string.error_synchronization, Snackbar.LENGTH_LONG)
.setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(exceptions)
@@ -232,7 +220,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
} else {
newMethod = CategorySortingMethod.SORT_LEXICOGRAPHICAL_ASC;
}
- db.modifyCategoryOrder(localAccount.getId(), methodOfCategory.first, newMethod);
+ mainViewModel.modifyCategoryOrder(localAccount.getId(), methodOfCategory.first, newMethod);
}
});
});
@@ -240,7 +228,6 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
mainViewModel.getCurrentAccount().observe(this, (a) -> {
fabCreate.hide();
localAccount = a;
- SingleAccountHelper.setCurrentAccount(getApplicationContext(), a.getAccountName());
Glide
.with(this)
.load(a.getUrl() + "/index.php/avatar/" + Uri.encode(a.getUserName()) + "/64")
@@ -250,11 +237,12 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
.into(activityBinding.launchAccountSwitcher);
try {
- BrandingUtil.saveBrandColors(this, localAccount.getColor(), localAccount.getTextColor());
ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext());
- new NotesListViewItemTouchHelper(ssoAccount, this, db, adapter, swipeRefreshLayout, coordinatorLayout, gridView)
+ new NotesListViewItemTouchHelper(ssoAccount, this, mainViewModel, this, adapter, swipeRefreshLayout, coordinatorLayout, gridView)
.attachToRecyclerView(listView);
- synchronize();
+ if (!mainViewModel.synchronize(ssoAccount)) {
+ BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
+ }
} catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
Log.i(TAG, "Tried to select account, but got an " + e.getClass().getSimpleName() + ". Asking for importing an account...");
handleNotAuthorizedAccount();
@@ -283,8 +271,6 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
menuAdapter.updateAccount(a);
}
});
-
- new Thread(() -> canMoveNoteToAnotherAccounts = db.getAccountDao().getAccountsCount() > 1).start();
}
@Override
@@ -292,7 +278,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
try {
ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext());
if (localAccount == null || !localAccount.getAccountName().equals(ssoAccount.name)) {
- Account account = db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name);
+ Account account = mainViewModel.getLocalAccountByAccountName(ssoAccount.name);
if (account == null) {
askForNewAccount(this);
} else {
@@ -301,7 +287,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
}
} catch (NoCurrentAccountSelectedException | NextcloudFilesAppAccountNotFoundException e) {
if (localAccount == null) {
- List<Account> localAccounts = db.getAccountDao().getAccounts();
+ List<Account> localAccounts = mainViewModel.getAccounts();
if (localAccounts.size() > 0) {
mainViewModel.postCurrentAccount(localAccount);
}
@@ -313,7 +299,9 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
// refresh and sync every time the activity gets
if (localAccount != null) {
- synchronize();
+ if (!mainViewModel.synchronize(ssoAccount)) {
+ BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
+ }
}
super.onResume();
}
@@ -414,39 +402,21 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
});
swipeRefreshLayout.setOnRefreshListener(() -> {
- if (ssoAccount == null) {
- askForNewAccount(this);
- } else {
- Log.i(TAG, "Clearing Glide memory cache");
- Glide.get(this).clearMemory();
- new Thread(() -> {
- Log.i(TAG, "Clearing Glide disk cache");
- Glide.get(getApplicationContext()).clearDiskCache();
- }).start();
- new Thread(() -> {
- Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name);
- final Capabilities capabilities;
- try {
- capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, localAccount.getCapabilitiesETag());
- db.getAccountDao().updateCapabilitiesETag(localAccount.getId(), capabilities.getETag());
- db.getAccountDao().updateBrand(localAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
- localAccount.setColor(capabilities.getColor());
- localAccount.setTextColor(capabilities.getTextColor());
- BrandingUtil.saveBrandColors(this, localAccount.getColor(), localAccount.getTextColor());
- db.updateApiVersion(localAccount.getId(), capabilities.getApiVersion());
- Log.i(TAG, capabilities.toString());
- } catch (Exception e) {
- if (e instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e).getStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
- Log.i(TAG, "Capabilities not modified.");
- } else {
- e.printStackTrace();
- }
- } finally {
- // Even if the capabilities endpoint makes trouble, we can still try to synchronize the notes
- synchronize();
- }
- }).start();
- }
+ Log.i(TAG, "Clearing Glide memory cache");
+ Glide.get(this).clearMemory();
+ new Thread(() -> {
+ Log.i(TAG, "Clearing Glide disk cache");
+ Glide.get(getApplicationContext()).clearDiskCache();
+ }).start();
+ Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name);
+ final LiveData<Boolean> syncLiveData = mainViewModel.performFullSynchronizationForCurrentAccount();
+ final Observer<Boolean> syncObserver = syncSuccess -> {
+ if (!syncSuccess) {
+ BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
+ }
+ syncLiveData.removeObservers(this);
+ };
+ syncLiveData.observe(this, syncObserver);
});
}
@@ -476,7 +446,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
}
default: {
if (item.getClass() == NavigationItem.CategoryNavigationItem.class) {
- mainViewModel.postSelectedCategory(new NavigationCategory(db.getCategoryDao().getCategory(((NavigationItem.CategoryNavigationItem) item).categoryId)));
+ mainViewModel.postSelectedCategory(new NavigationCategory(mainViewModel.getCategory(((NavigationItem.CategoryNavigationItem) item).categoryId)));
} else {
throw new IllegalStateException(NavigationItem.class.getSimpleName() + " type is " + DEFAULT_CATEGORY + ", but item is not of type " + NavigationItem.CategoryNavigationItem.class.getSimpleName() + ".");
}
@@ -603,7 +573,6 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
recreate(); // TODO
askForNewAccount(this);
}
- new Thread(() -> canMoveNoteToAnotherAccounts = db.getAccountDao().getAccountsCount() > 1).start();
break;
}
default: {
@@ -615,21 +584,20 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
try {
Log.i(TAG, "Refreshing capabilities for " + ssoAccount.name);
final Capabilities capabilities = CapabilitiesClient.getCapabilities(getApplicationContext(), ssoAccount, null);
- LiveData<Account> createLiveData = db.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities);
+ LiveData<Account> createLiveData = mainViewModel.addAccount(ssoAccount.url, ssoAccount.userId, ssoAccount.name, capabilities);
runOnUiThread(() -> createLiveData.observe(this, (account) -> {
- new Thread(() -> canMoveNoteToAnotherAccounts = db.getAccountDao().getAccountsCount() > 1).start();
Log.i(TAG, capabilities.toString());
- runOnUiThread(() -> mainViewModel.postCurrentAccount(db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name)));
+ runOnUiThread(() -> mainViewModel.postCurrentAccount(mainViewModel.getLocalAccountByAccountName(ssoAccount.name)));
}));
} catch (SQLiteException e) {
// Happens when upgrading from version ≤ 1.0.1 and importing the account
- runOnUiThread(() -> mainViewModel.postCurrentAccount(db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name)));
+ runOnUiThread(() -> mainViewModel.postCurrentAccount(mainViewModel.getLocalAccountByAccountName(ssoAccount.name)));
} catch (Exception e) {
// Happens when importing an already existing account the second time
- if (e instanceof TokenMismatchException && db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name) != null) {
+ if (e instanceof TokenMismatchException && mainViewModel.getLocalAccountByAccountName(ssoAccount.name) != null) {
Log.w(TAG, "Received " + TokenMismatchException.class.getSimpleName() + " and the given ssoAccount.name (" + ssoAccount.name + ") does already exist in the database. Assume that this account has already been imported.");
runOnUiThread(() -> {
- mainViewModel.postCurrentAccount(db.getAccountDao().getLocalAccountByAccountName(ssoAccount.name));
+ mainViewModel.postCurrentAccount(mainViewModel.getLocalAccountByAccountName(ssoAccount.name));
// TODO there is already a sync in progress and results in displaying a TokenMissMatchException snackbar which conflicts with this one
coordinatorLayout.post(() -> BrandedSnackbar.make(coordinatorLayout, R.string.account_already_imported, Snackbar.LENGTH_LONG).show());
});
@@ -676,8 +644,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onNoteFavoriteClick(int position, View view) {
- NoteWithCategory note = (NoteWithCategory) adapter.getItem(position);
- db.toggleFavoriteAndSync(ssoAccount, note.getId());
+ mainViewModel.toggleFavoriteAndSync(ssoAccount, ((NoteWithCategory) adapter.getItem(position)).getId());
adapter.notifyItemChanged(position);
}
@@ -686,9 +653,7 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
boolean selected = adapter.select(position);
if (selected) {
v.setSelected(true);
- mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(
- this, coordinatorLayout, db, localAccount.getId(), canMoveNoteToAnotherAccounts, adapter, listView, getSupportFragmentManager(), activityBinding.searchView
- ));
+ mActionMode = startSupportActionMode(new MultiSelectedActionModeCallback(this, coordinatorLayout, mainViewModel, this, localAccount.getId(), canMoveNoteToAnotherAccounts, adapter, listView, getSupportFragmentManager(), activityBinding.searchView));
int checkedItemCount = adapter.getSelected().size();
mActionMode.setTitle(getResources().getQuantityString(R.plurals.ab_selected, checkedItemCount, checkedItemCount));
}
@@ -719,23 +684,6 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
activityBinding.searchView.setIconified(disableSearch);
}
- private void synchronize() {
- NoteServerSyncHelper syncHelper = db.getNoteServerSyncHelper();
- if (!syncHelper.isSyncPossible()) {
- syncHelper.updateNetworkStatus();
- }
- if (syncHelper.isSyncPossible()) {
- syncHelper.scheduleSync(ssoAccount, false);
- } else { // Sync is not possible
- if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) {
- Log.d(TAG, "Network is connected, but sync is not possible");
- } else {
- Log.d(TAG, "Sync is not possible, because network is not connected");
- BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
- }
- }
- }
-
@Override
public void addAccount() {
askForNewAccount(this);
@@ -749,10 +697,10 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onAccountDeleted(Account localAccount) {
- final LiveData<Void> deleteLiveData = db.deleteAccount(localAccount);
+ final LiveData<Void> deleteLiveData = mainViewModel.deleteAccount(localAccount);
deleteLiveData.observe(this, (v) -> {
if (localAccount.getId() == this.localAccount.getId()) {
- List<Account> remainingAccounts = db.getAccountDao().getAccounts();
+ List<Account> remainingAccounts = mainViewModel.getAccounts();
if (remainingAccounts.size() > 0) {
mainViewModel.postCurrentAccount(remainingAccounts.get(0));
} else {
@@ -767,15 +715,15 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A
@Override
public void onAccountPicked(@NonNull Account account) {
for (Integer i : adapter.getSelected()) {
- final LiveData<NoteWithCategory> moveLiveData = db.moveNoteToAnotherAccount(ssoAccount, (NoteWithCategory) adapter.getItem(i), account.getId());
+ final LiveData<NoteWithCategory> moveLiveData = mainViewModel.moveNoteToAnotherAccount(ssoAccount, (NoteWithCategory) adapter.getItem(i), account.getId());
moveLiveData.observe(this, (v) -> moveLiveData.removeObservers(this));
}
}
@Override
public void onCategoryChosen(String category) {
- for (Integer i : new ArrayList<>(adapter.getSelected())) {
- db.setCategory(ssoAccount, localAccount.getId(), ((NoteWithCategory) adapter.getItem(i)).getId(), category);
+ for (Integer i : adapter.getSelected()) {
+ mainViewModel.setCategory(ssoAccount, localAccount.getId(), ((NoteWithCategory) adapter.getItem(i)).getId(), category);
}
}
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java
index e7b186ed..e49b0726 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MainViewModel.java
@@ -8,23 +8,38 @@ import androidx.annotation.AnyThread;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import androidx.core.util.Pair;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
+import com.nextcloud.android.sso.AccountImporter;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
+import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException;
+import com.nextcloud.android.sso.helper.SingleAccountHelper;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
+
+import java.net.HttpURLConnection;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import it.niedermann.owncloud.notes.R;
+import it.niedermann.owncloud.notes.branding.BrandingUtil;
import it.niedermann.owncloud.notes.main.navigation.NavigationAdapter;
import it.niedermann.owncloud.notes.main.navigation.NavigationItem;
+import it.niedermann.owncloud.notes.persistence.CapabilitiesClient;
+import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper;
import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Category;
import it.niedermann.owncloud.notes.persistence.entity.CategoryWithNotesCount;
import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory;
+import it.niedermann.owncloud.notes.persistence.entity.SingleNoteWidgetData;
+import it.niedermann.owncloud.notes.shared.model.Capabilities;
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
+import it.niedermann.owncloud.notes.shared.model.ISyncCallback;
import it.niedermann.owncloud.notes.shared.model.Item;
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
@@ -73,6 +88,8 @@ public class MainViewModel extends AndroidViewModel {
}
public void postCurrentAccount(@NonNull Account account) {
+ BrandingUtil.saveBrandColors(getApplication(), account.getColor(), account.getTextColor());
+ SingleAccountHelper.setCurrentAccount(getApplication(), account.getAccountName());
this.currentAccount.postValue(account);
}
@@ -298,6 +315,31 @@ public class MainViewModel extends AndroidViewModel {
return items;
}
+ public void modifyCategoryOrder(long accountId, @NonNull NavigationCategory selectedCategory, @NonNull CategorySortingMethod sortingMethod) {
+ db.modifyCategoryOrder(accountId, selectedCategory, sortingMethod);
+ }
+
+ /**
+ * @return <code>true</code>, if a synchronization could successfully be triggered.
+ */
+ public boolean synchronize(SingleSignOnAccount ssoAccount) {
+ NoteServerSyncHelper syncHelper = db.getNoteServerSyncHelper();
+ if (!syncHelper.isSyncPossible()) {
+ syncHelper.updateNetworkStatus();
+ }
+ if (syncHelper.isSyncPossible()) {
+ syncHelper.scheduleSync(ssoAccount, false);
+ return true;
+ } else { // Sync is not possible
+ if (syncHelper.isNetworkConnected() && syncHelper.isSyncOnlyOnWifi()) {
+ Log.d(TAG, "Network is connected, but sync is not possible");
+ } else {
+ Log.d(TAG, "Sync is not possible, because network is not connected");
+ }
+ }
+ return false;
+ }
+
public LiveData<Boolean> getSyncStatus() {
return db.getNoteServerSyncHelper().getSyncStatus();
}
@@ -305,4 +347,112 @@ public class MainViewModel extends AndroidViewModel {
public LiveData<ArrayList<Throwable>> getSyncErrors() {
return db.getNoteServerSyncHelper().getSyncErrors();
}
+
+ public LiveData<Boolean> hasMultipleAccountsConfigured() {
+ return map(db.getAccountDao().getAccountsCountLiveData(), (counter) -> counter != null && counter > 1);
+ }
+
+ public LiveData<Boolean> performFullSynchronizationForCurrentAccount() {
+ final MutableLiveData<Boolean> insufficientInformation = new MutableLiveData<>();
+ return switchMap(getCurrentAccount(), currentAccount -> {
+ Log.v(TAG, "[performFullSynchronizationForCurrentAccount] - currentAccount: " + currentAccount);
+ if (currentAccount == null) {
+ return insufficientInformation;
+ } else {
+ MutableLiveData<Boolean> syncSuccess = new MutableLiveData<>();
+ new Thread(() -> {
+ try {
+ SingleSignOnAccount ssoAccount = AccountImporter.getSingleSignOnAccount(getApplication(), currentAccount.getAccountName());
+ final Capabilities capabilities;
+ try {
+ capabilities = CapabilitiesClient.getCapabilities(getApplication(), ssoAccount, currentAccount.getCapabilitiesETag());
+ db.getAccountDao().updateCapabilitiesETag(currentAccount.getId(), capabilities.getETag());
+ db.getAccountDao().updateBrand(currentAccount.getId(), capabilities.getColor(), capabilities.getTextColor());
+ currentAccount.setColor(capabilities.getColor());
+ currentAccount.setTextColor(capabilities.getTextColor());
+ BrandingUtil.saveBrandColors(getApplication(), currentAccount.getColor(), currentAccount.getTextColor());
+ db.updateApiVersion(currentAccount.getId(), capabilities.getApiVersion());
+ Log.i(TAG, capabilities.toString());
+ } catch (Exception e) {
+ if (e instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e).getStatusCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
+ Log.i(TAG, "Capabilities not modified.");
+ } else {
+ e.printStackTrace();
+ }
+ }
+ // Even if the capabilities endpoint makes trouble, we can still try to synchronize the notes
+ syncSuccess.postValue(synchronize(ssoAccount));
+ } catch (NextcloudFilesAppAccountNotFoundException e) {
+ e.printStackTrace();
+ // TODO should we just remove this account from the database?
+ syncSuccess.postValue(true);
+ }
+ }).start();
+ return syncSuccess;
+ }
+ });
+ }
+
+ @WorkerThread
+ public Account getLocalAccountByAccountName(String accountName) {
+ return db.getAccountDao().getLocalAccountByAccountName(accountName);
+ }
+
+ @WorkerThread
+ public List<Account> getAccounts() {
+ return db.getAccountDao().getAccounts();
+ }
+
+ @AnyThread
+ public void setCategory(SingleSignOnAccount ssoAccount, long accountId, Long noteId, @NonNull String category) {
+ db.setCategory(ssoAccount, accountId, noteId, category);
+ }
+
+ @AnyThread
+ public LiveData<NoteWithCategory> moveNoteToAnotherAccount(SingleSignOnAccount ssoAccount, NoteWithCategory note, long newAccountId) {
+ return db.moveNoteToAnotherAccount(ssoAccount, note, newAccountId);
+ }
+
+ @WorkerThread
+ public Category getCategory(long id) {
+ return db.getCategoryDao().getCategory(id);
+ }
+
+ @AnyThread
+ public LiveData<Void> deleteAccount(@NonNull Account account) {
+ return db.deleteAccount(account);
+ }
+
+ @AnyThread
+ public void toggleFavoriteAndSync(SingleSignOnAccount ssoAccount, long noteId) {
+ db.toggleFavoriteAndSync(ssoAccount, noteId);
+ }
+
+ @AnyThread
+ public void deleteNoteAndSync(SingleSignOnAccount ssoAccount, long id) {
+ db.deleteNoteAndSync(ssoAccount, id);
+ }
+
+ @AnyThread
+ public LiveData<Account> addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities) {
+ return db.addAccount(url, username, accountName, capabilities);
+ }
+
+ @WorkerThread
+ public NoteWithCategory getNoteWithCategory(long accountId, long id) {
+ return db.getNoteDao().getNoteWithCategory(accountId, id);
+ }
+
+ @AnyThread
+ public LiveData<NoteWithCategory> addNoteAndSync(SingleSignOnAccount ssoAccount, long accountId, NoteWithCategory note) {
+ return db.addNoteAndSync(ssoAccount, accountId, note);
+ }
+
+ public void updateNoteAndSync(SingleSignOnAccount ssoAccount, @NonNull Account localAccount, @NonNull NoteWithCategory oldNote, @Nullable String newContent, @Nullable String newTitle, @Nullable ISyncCallback callback) {
+ db.updateNoteAndSync(ssoAccount, localAccount, oldNote, newContent, newTitle, callback);
+ }
+
+ public void createOrUpdateSingleNoteWidgetData(SingleNoteWidgetData data) {
+ db.getWidgetSingleNoteDao().createOrUpdateSingleNoteWidgetData(data);
+ }
}
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java b/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java
index 2c9ea75b..cf83eb05 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/MultiSelectedActionModeCallback.java
@@ -9,11 +9,14 @@ import android.view.MenuItem;
import android.view.View;
import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.view.ActionMode.Callback;
import androidx.appcompat.widget.SearchView;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
@@ -37,11 +40,15 @@ import it.niedermann.owncloud.notes.shared.util.ShareUtil;
public class MultiSelectedActionModeCallback implements Callback {
@ColorInt
- private int colorAccent;
-
+ private final int colorAccent;
+ @NonNull
private final Context context;
+ @NonNull
private final View view;
- private final NotesDatabase db;
+ @NonNull
+ private final MainViewModel mainViewModel;
+ @NonNull
+ private final LifecycleOwner lifecycleOwner;
private final long currentLocalAccountId;
private final boolean canMoveNoteToAnotherAccounts;
private final ItemAdapter adapter;
@@ -50,10 +57,11 @@ public class MultiSelectedActionModeCallback implements Callback {
private final SearchView searchView;
public MultiSelectedActionModeCallback(
- Context context, View view, NotesDatabase db, long currentLocalAccountId, boolean canMoveNoteToAnotherAccounts, ItemAdapter adapter, RecyclerView recyclerView, FragmentManager fragmentManager, SearchView searchView) {
+ @NonNull Context context, @NonNull View view, @NonNull MainViewModel mainViewModel, @NonNull LifecycleOwner lifecycleOwner, long currentLocalAccountId, boolean canMoveNoteToAnotherAccounts, ItemAdapter adapter, RecyclerView recyclerView, FragmentManager fragmentManager, SearchView searchView) {
this.context = context;
this.view = view;
- this.db = db;
+ this.mainViewModel = mainViewModel;
+ this.lifecycleOwner = lifecycleOwner;
this.currentLocalAccountId = currentLocalAccountId;
this.canMoveNoteToAnotherAccounts = canMoveNoteToAnotherAccounts;
this.adapter = adapter;
@@ -94,71 +102,70 @@ public class MultiSelectedActionModeCallback implements Callback {
*/
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_delete:
- try {
- SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);
- List<NoteWithCategory> deletedNotes = new ArrayList<>();
- List<Integer> selection = adapter.getSelected();
- for (Integer i : selection) {
- NoteWithCategory note = (NoteWithCategory) adapter.getItem(i);
- deletedNotes.add(db.getNoteDao().getNoteWithCategory(note.getAccountId(), note.getId()));
- db.deleteNoteAndSync(ssoAccount, note.getId());
- }
- mode.finish(); // Action picked, so close the CAB
- //after delete selection has to be cleared
- searchView.setIconified(true);
- String deletedSnackbarTitle = deletedNotes.size() == 1
- ? context.getString(R.string.action_note_deleted, deletedNotes.get(0).getTitle())
- : context.getResources().getQuantityString(R.plurals.bulk_notes_deleted, deletedNotes.size(), deletedNotes.size());
- BrandedSnackbar.make(view, deletedSnackbarTitle, Snackbar.LENGTH_LONG)
- .setAction(R.string.action_undo, (View v) -> {
- db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, () -> {
- });
- for (NoteWithCategory deletedNote : deletedNotes) {
- db.addNoteAndSync(ssoAccount, deletedNote.getAccountId(), deletedNote);
- }
- String restoreSnackbarTitle = deletedNotes.size() == 1
- ? context.getString(R.string.action_note_restored, deletedNotes.get(0).getTitle())
- : context.getResources().getQuantityString(R.plurals.bulk_notes_restored, deletedNotes.size(), deletedNotes.size());
- BrandedSnackbar.make(view, restoreSnackbarTitle, Snackbar.LENGTH_SHORT)
- .show();
- })
- .show();
- } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
- e.printStackTrace();
+ int itemId = item.getItemId();
+ if (itemId == R.id.menu_delete) {
+ try {
+ SingleSignOnAccount ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(context);
+ List<NoteWithCategory> deletedNotes = new ArrayList<>();
+ List<Integer> selection = adapter.getSelected();
+ for (Integer i : selection) {
+ NoteWithCategory note = (NoteWithCategory) adapter.getItem(i);
+ deletedNotes.add(mainViewModel.getNoteWithCategory(note.getAccountId(), note.getId()));
+ mainViewModel.deleteNoteAndSync(ssoAccount, note.getId());
}
- return true;
- case R.id.menu_move:
- AccountPickerDialogFragment
- .newInstance(currentLocalAccountId)
- .show(fragmentManager, MainActivity.class.getSimpleName());
- return true;
- case R.id.menu_share:
- final String subject = (adapter.getSelected().size() == 1)
- ? ((NoteWithCategory) adapter.getItem(adapter.getSelected().get(0))).getTitle()
- : context.getResources().getQuantityString(R.plurals.share_multiple, adapter.getSelected().size(), adapter.getSelected().size());
- final StringBuilder noteContents = new StringBuilder();
- for (Integer i : adapter.getSelected()) {
- final NoteWithCategory noteWithoutContent = (NoteWithCategory) adapter.getItem(i);
- final String tempFullNote = db.getNoteDao().getNoteWithCategory(noteWithoutContent.getAccountId(), noteWithoutContent.getId()).getContent();
- if (!TextUtils.isEmpty(tempFullNote)) {
- if (noteContents.length() > 0) {
- noteContents.append("\n\n");
- }
- noteContents.append(tempFullNote);
+ mode.finish(); // Action picked, so close the CAB
+ //after delete selection has to be cleared
+ searchView.setIconified(true);
+ String deletedSnackbarTitle = deletedNotes.size() == 1
+ ? context.getString(R.string.action_note_deleted, deletedNotes.get(0).getTitle())
+ : context.getResources().getQuantityString(R.plurals.bulk_notes_deleted, deletedNotes.size(), deletedNotes.size());
+ BrandedSnackbar.make(view, deletedSnackbarTitle, Snackbar.LENGTH_LONG)
+ .setAction(R.string.action_undo, (View v) -> {
+ for (NoteWithCategory deletedNote : deletedNotes) {
+ final LiveData<NoteWithCategory> undoLiveData = mainViewModel.addNoteAndSync(ssoAccount, deletedNote.getAccountId(), deletedNote);
+ undoLiveData.observe(lifecycleOwner, (o) -> undoLiveData.removeObservers(lifecycleOwner));
+ }
+ String restoreSnackbarTitle = deletedNotes.size() == 1
+ ? context.getString(R.string.action_note_restored, deletedNotes.get(0).getTitle())
+ : context.getResources().getQuantityString(R.plurals.bulk_notes_restored, deletedNotes.size(), deletedNotes.size());
+ BrandedSnackbar.make(view, restoreSnackbarTitle, Snackbar.LENGTH_SHORT)
+ .show();
+ })
+ .show();
+ } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
+ e.printStackTrace();
+ }
+ return true;
+ } else if (itemId == R.id.menu_move) {
+ AccountPickerDialogFragment
+ .newInstance(currentLocalAccountId)
+ .show(fragmentManager, MainActivity.class.getSimpleName());
+ return true;
+ } else if (itemId == R.id.menu_share) {
+ final String subject = (adapter.getSelected().size() == 1)
+ ? ((NoteWithCategory) adapter.getItem(adapter.getSelected().get(0))).getTitle()
+ : context.getResources().getQuantityString(R.plurals.share_multiple, adapter.getSelected().size(), adapter.getSelected().size());
+ final StringBuilder noteContents = new StringBuilder();
+ for (Integer i : adapter.getSelected()) {
+ final NoteWithCategory noteWithoutContent = (NoteWithCategory) adapter.getItem(i);
+ final String tempFullNote = mainViewModel.getNoteWithCategory(noteWithoutContent.getAccountId(), noteWithoutContent.getId()).getContent();
+ if (!TextUtils.isEmpty(tempFullNote)) {
+ if (noteContents.length() > 0) {
+ noteContents.append("\n\n");
}
+ noteContents.append(tempFullNote);
}
- ShareUtil.openShareDialog(context, subject, noteContents.toString());
- return true;
- case R.id.menu_category:
- // TODO detect whether all selected notes do have the same category - in this case preselect it
- CategoryDialogFragment
- .newInstance(currentLocalAccountId, "")
- .show(fragmentManager, CategoryDialogFragment.class.getSimpleName());
- default:
- return false;
+ }
+ ShareUtil.openShareDialog(context, subject, noteContents.toString());
+ return true;
+ } else if (itemId == R.id.menu_category) {// TODO detect whether all selected notes do have the same category - in this case preselect it
+ CategoryDialogFragment
+ .newInstance(currentLocalAccountId, "")
+ .show(fragmentManager, CategoryDialogFragment.class.getSimpleName());
+
+ return false;
}
+ return false;
}
@Override
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java
index 8c5a970c..39f640a9 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/ItemAdapter.java
@@ -31,7 +31,6 @@ import it.niedermann.owncloud.notes.main.items.list.NoteViewHolderWithExcerpt;
import it.niedermann.owncloud.notes.main.items.list.NoteViewHolderWithoutExcerpt;
import it.niedermann.owncloud.notes.main.items.section.SectionItem;
import it.niedermann.owncloud.notes.main.items.section.SectionViewHolder;
-import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.persistence.entity.NoteWithCategory;
import it.niedermann.owncloud.notes.shared.model.Item;
import it.niedermann.owncloud.notes.shared.model.NoteClickListener;
@@ -94,39 +93,21 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
notifyDataSetChanged();
}
- /**
- * Adds the given note to the top of the list.
- *
- * @param note Note that should be added.
- */
- public void add(@NonNull NoteWithCategory note) {
- itemList.add(0, note);
- notifyItemInserted(0);
- notifyItemChanged(0);
- }
-
- /**
- * Removes all items from the adapter.
- */
- public void removeAll() {
- itemList.clear();
- notifyDataSetChanged();
- }
-
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (gridView) {
switch (viewType) {
case TYPE_SECTION: {
- return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(LayoutInflater.from(parent.getContext())));
+ return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
}
case TYPE_NOTE_ONLY_TITLE: {
- return new NoteViewGridHolderOnlyTitle(ItemNotesListNoteItemGridOnlyTitleBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false), noteClickListener, monospace, fontSize);
+ return new NoteViewGridHolderOnlyTitle(ItemNotesListNoteItemGridOnlyTitleBinding.inflate(inflater, parent, false), noteClickListener, monospace, fontSize);
}
case TYPE_NOTE_WITH_EXCERPT:
case TYPE_NOTE_WITHOUT_EXCERPT: {
- return new NoteViewGridHolder(ItemNotesListNoteItemGridBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false), noteClickListener, monospace, fontSize);
+ return new NoteViewGridHolder(ItemNotesListNoteItemGridBinding.inflate(inflater, parent, false), noteClickListener, monospace, fontSize);
}
default: {
throw new IllegalArgumentException("Not supported viewType: " + viewType);
@@ -135,14 +116,14 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
} else {
switch (viewType) {
case TYPE_SECTION: {
- return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(LayoutInflater.from(parent.getContext())));
+ return new SectionViewHolder(ItemNotesListSectionItemBinding.inflate(inflater));
}
case TYPE_NOTE_WITH_EXCERPT: {
- return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false), noteClickListener);
+ return new NoteViewHolderWithExcerpt(ItemNotesListNoteItemWithExcerptBinding.inflate(inflater, parent, false), noteClickListener);
}
case TYPE_NOTE_ONLY_TITLE:
case TYPE_NOTE_WITHOUT_EXCERPT: {
- return new NoteViewHolderWithoutExcerpt(ItemNotesListNoteItemWithoutExcerptBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false), noteClickListener);
+ return new NoteViewHolderWithoutExcerpt(ItemNotesListNoteItemWithoutExcerptBinding.inflate(inflater, parent, false), noteClickListener);
}
default: {
throw new IllegalArgumentException("Not supported viewType: " + viewType);
@@ -177,7 +158,7 @@ public class ItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> i
if (viewHolder != null) {
viewHolder.itemView.setSelected(false);
} else {
- Log.w(TAG, "Could not found viewholder to remove selection");
+ Log.w(TAG, "Could not found " + RecyclerView.ViewHolder.class.getSimpleName() + " to remove selection");
}
}
selected.clear();
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NotesListViewItemTouchHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NotesListViewItemTouchHelper.java
index 24a20777..0f68ac35 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NotesListViewItemTouchHelper.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/main/items/list/NotesListViewItemTouchHelper.java
@@ -9,6 +9,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@@ -18,6 +20,7 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.branding.BrandedSnackbar;
+import it.niedermann.owncloud.notes.main.MainViewModel;
import it.niedermann.owncloud.notes.main.items.ItemAdapter;
import it.niedermann.owncloud.notes.main.items.NoteViewHolder;
import it.niedermann.owncloud.notes.main.items.section.SectionViewHolder;
@@ -32,7 +35,8 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper {
public NotesListViewItemTouchHelper(
@NonNull SingleSignOnAccount ssoAccount,
@NonNull Context context,
- @NonNull NotesDatabase db,
+ @NonNull MainViewModel mainViewModel,
+ @NonNull LifecycleOwner lifecycleOwner,
@NonNull ItemAdapter adapter,
@Nullable SwipeRefreshLayout swipeRefreshLayout,
@Nullable View view,
@@ -70,19 +74,16 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper {
switch (direction) {
case ItemTouchHelper.LEFT:
final NoteWithCategory dbNoteWithoutContent = (NoteWithCategory) adapter.getItem(viewHolder.getAdapterPosition());
- final NoteWithCategory dbNote = db.getNoteDao().getNoteWithCategory(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId());
- db.deleteNoteAndSync(ssoAccount, dbNote.getId());
-// FIXME
-// adapter.remove(dbNote);
+ final NoteWithCategory dbNote = mainViewModel.getNoteWithCategory(dbNoteWithoutContent.getAccountId(), dbNoteWithoutContent.getId());
+ mainViewModel.deleteNoteAndSync(ssoAccount, dbNote.getId());
Log.v(TAG, "Item deleted through swipe ----------------------------------------------");
if (view == null) {
Toast.makeText(context, context.getString(R.string.action_note_deleted, dbNote.getTitle()), Toast.LENGTH_LONG).show();
} else {
BrandedSnackbar.make(view, context.getString(R.string.action_note_deleted, dbNote.getTitle()), UNDO_DURATION)
.setAction(R.string.action_undo, (View v) -> {
- db.getNoteServerSyncHelper().addCallbackPush(ssoAccount, () -> {
- });
- db.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote);
+ final LiveData<NoteWithCategory> undoLiveData = mainViewModel.addNoteAndSync(ssoAccount, dbNote.getAccountId(), dbNote);
+ undoLiveData.observe(lifecycleOwner, (o) -> undoLiveData.removeObservers(lifecycleOwner));
BrandedSnackbar.make(view, context.getString(R.string.action_note_restored, dbNote.getTitle()), Snackbar.LENGTH_SHORT)
.show();
})
@@ -91,7 +92,7 @@ public class NotesListViewItemTouchHelper extends ItemTouchHelper {
break;
case ItemTouchHelper.RIGHT:
final NoteWithCategory adapterNote = (NoteWithCategory) adapter.getItem(viewHolder.getAdapterPosition());
- db.toggleFavoriteAndSync(ssoAccount, adapterNote.getId());
+ mainViewModel.toggleFavoriteAndSync(ssoAccount, adapterNote.getId());
break;
default:
//NoOp
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java
index f244ab34..c716aae7 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/dao/AccountDao.java
@@ -1,5 +1,6 @@
package it.niedermann.owncloud.notes.persistence.dao;
+import androidx.annotation.ColorInt;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
@@ -34,8 +35,11 @@ public interface AccountDao {
@Query("SELECT COUNT(*) FROM Account")
Integer getAccountsCount();
- @Query("UPDATE Account SET COLOR = :color AND TEXTCOLOR = :textColor WHERE id = :id")
- void updateBrand(long id, Integer color, Integer textColor);
+ @Query("SELECT COUNT(*) FROM Account")
+ LiveData<Integer> getAccountsCountLiveData();
+
+ @Query("UPDATE Account SET COLOR = :color, TEXTCOLOR = :textColor WHERE id = :id")
+ void updateBrand(long id, @ColorInt Integer color, @ColorInt Integer textColor);
@Query("UPDATE Account SET ETAG = :eTag WHERE ID = :id")
void updateETag(long id, String eTag);
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetConfigurationActivity.java
index 12d6d371..375fa27a 100644
--- a/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetConfigurationActivity.java
+++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/singlenote/SingleNoteWidgetConfigurationActivity.java
@@ -54,7 +54,7 @@ public class SingleNoteWidgetConfigurationActivity extends MainActivity {
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
try {
- db.getWidgetSingleNoteDao().createOrUpdateSingleNoteWidgetData(
+ mainViewModel.createOrUpdateSingleNoteWidgetData(
new SingleNoteWidgetData(
appWidgetId,
note.getAccountId(),
diff --git a/app/src/main/res/layout/activity_notes_list_view.xml b/app/src/main/res/layout/activity_notes_list_view.xml
index 34b3907a..781b02c0 100644
--- a/app/src/main/res/layout/activity_notes_list_view.xml
+++ b/app/src/main/res/layout/activity_notes_list_view.xml
@@ -141,6 +141,7 @@
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_create"
style="@style/fab"
+ android:contentDescription="@string/action_create"
android:title="@string/action_create"
app:backgroundTint="@color/defaultBrand"
app:srcCompat="@drawable/ic_add_white_24dp" />