diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-10-16 17:13:23 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2020-10-16 17:13:23 +0300 |
commit | 8f941964ba63205b95eb5254f1ce0c8fcd318b77 (patch) | |
tree | d70069d623f3189313e1264b3fa6a51d41ef775b /app/src/main | |
parent | 3159c4dfdeddd386fb7af586b44c4fd03894550b (diff) |
Fix branding
Diffstat (limited to 'app/src/main')
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" /> |