use of it.niedermann.owncloud.notes.persistence.entity.Account in project nextcloud-notes by stefan-niedermann.
the class MainActivity method onCreate.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainViewModel = new ViewModelProvider(this).get(MainViewModel.class);
categoryViewModel = new ViewModelProvider(this).get(CategoryViewModel.class);
CapabilitiesWorker.update(this);
binding = DrawerLayoutBinding.inflate(getLayoutInflater());
activityBinding = ActivityNotesListViewBinding.bind(binding.activityNotesListView.getRoot());
setContentView(binding.getRoot());
this.coordinatorLayout = binding.activityNotesListView.activityNotesListView;
this.swipeRefreshLayout = binding.activityNotesListView.swiperefreshlayout;
this.fabCreate = binding.activityNotesListView.fabCreate;
this.listView = binding.activityNotesListView.recyclerView;
gridView = isGridViewEnabled();
if (!gridView || isDarkThemeActive(this)) {
activityBinding.activityNotesListView.setBackgroundColor(ContextCompat.getColor(this, R.color.primary));
}
setupToolbars();
setupNavigationList();
setupNotesList();
mainViewModel.getAccountsCount().observe(this, (count) -> {
if (count == 0) {
startActivityForResult(new Intent(this, ImportAccountActivity.class), ImportAccountActivity.REQUEST_CODE_IMPORT_ACCOUNT);
} else {
executor.submit(() -> {
try {
final var account = mainViewModel.getLocalAccountByAccountName(SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext()).name);
runOnUiThread(() -> mainViewModel.postCurrentAccount(account));
} catch (NextcloudFilesAppAccountNotFoundException e) {
// Verbose log output for https://github.com/stefan-niedermann/nextcloud-notes/issues/1256
runOnUiThread(() -> new AlertDialog.Builder(this).setTitle(NextcloudFilesAppAccountNotFoundException.class.getSimpleName()).setMessage(R.string.backup).setPositiveButton(R.string.simple_backup, (a, b) -> executor.submit(() -> {
final var modifiedNotes = new LinkedList<Note>();
for (final var account : mainViewModel.getAccounts()) {
modifiedNotes.addAll(mainViewModel.getLocalModifiedNotes(account.getId()));
}
if (modifiedNotes.size() == 1) {
final var note = modifiedNotes.get(0);
ShareUtil.openShareDialog(this, note.getTitle(), note.getContent());
} else {
ShareUtil.openShareDialog(this, getResources().getQuantityString(R.plurals.share_multiple, modifiedNotes.size(), modifiedNotes.size()), mainViewModel.collectNoteContents(modifiedNotes.stream().map(Note::getId).collect(Collectors.toList())));
}
})).setNegativeButton(R.string.simple_error, (a, b) -> {
final var ssoPreferences = AccountImporter.getSharedPreferences(getApplicationContext());
final var ssoPreferencesString = new StringBuilder().append("Current SSO account: ").append(ssoPreferences.getString("PREF_CURRENT_ACCOUNT_STRING", null)).append("\n").append("\n").append("SSO SharedPreferences: ").append("\n");
for (final var entry : ssoPreferences.getAll().entrySet()) {
ssoPreferencesString.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
ssoPreferencesString.append("\n").append("Available accounts in DB: ").append(TextUtils.join(", ", mainViewModel.getAccounts().stream().map(Account::getAccountName).collect(Collectors.toList())));
runOnUiThread(() -> ExceptionDialogFragment.newInstance(new RuntimeException(e.getMessage(), new RuntimeException(ssoPreferencesString.toString(), e))).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
}).show());
} catch (NoCurrentAccountSelectedException e) {
runOnUiThread(() -> ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
}
});
}
});
mainViewModel.hasMultipleAccountsConfigured().observe(this, hasMultipleAccountsConfigured -> canMoveNoteToAnotherAccounts = hasMultipleAccountsConfigured);
mainViewModel.getSyncStatus().observe(this, syncStatus -> swipeRefreshLayout.setRefreshing(syncStatus));
mainViewModel.getSyncErrors().observe(this, exceptions -> {
if (mainViewModel.containsNonInfrastructureRelatedItems(exceptions)) {
BrandedSnackbar.make(coordinatorLayout, R.string.error_synchronization, Snackbar.LENGTH_LONG).setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(exceptions).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())).show();
}
});
mainViewModel.getSelectedCategory().observe(this, (selectedCategory) -> {
binding.activityNotesListView.emptyContentView.getRoot().setVisibility(GONE);
adapter.setShowCategory(selectedCategory.getType() == RECENT || selectedCategory.getType() == FAVORITES);
fabCreate.show();
switch(selectedCategory.getType()) {
case RECENT:
{
activityBinding.searchText.setText(getString(R.string.search_in_all));
break;
}
case FAVORITES:
{
activityBinding.searchText.setText(getString(R.string.search_in_category, getString(R.string.label_favorites)));
break;
}
case UNCATEGORIZED:
{
activityBinding.searchText.setText(getString(R.string.search_in_category, getString(R.string.action_uncategorized)));
break;
}
case DEFAULT_CATEGORY:
default:
{
final String category = selectedCategory.getCategory();
if (category == null) {
throw new IllegalStateException(NavigationCategory.class.getSimpleName() + " type is " + DEFAULT_CATEGORY + ", but category is null.");
}
activityBinding.searchText.setText(getString(R.string.search_in_category, NoteUtil.extendCategory(category)));
break;
}
}
fabCreate.setOnClickListener((View view) -> {
final var createIntent = new Intent(getApplicationContext(), EditNoteActivity.class);
createIntent.putExtra(EditNoteActivity.PARAM_CATEGORY, selectedCategory);
if (activityBinding.searchView.getQuery().length() > 0) {
createIntent.putExtra(EditNoteActivity.PARAM_CONTENT, activityBinding.searchView.getQuery().toString());
invalidateOptionsMenu();
}
startActivityForResult(createIntent, REQUEST_CODE_CREATE_NOTE);
});
});
mainViewModel.getNotesListLiveData().observe(this, notes -> {
// https://stackoverflow.com/a/37342327
itemTouchHelper.attachToRecyclerView(null);
itemTouchHelper.attachToRecyclerView(listView);
adapter.setItemList(notes);
binding.activityNotesListView.progressCircular.setVisibility(GONE);
binding.activityNotesListView.emptyContentView.getRoot().setVisibility(notes.size() > 0 ? GONE : VISIBLE);
// Remove deleted notes from the selection
if (tracker.hasSelection()) {
final var deletedNotes = new LinkedList<Long>();
for (final var id : tracker.getSelection()) {
if (notes.stream().filter(item -> !item.isSection()).map(item -> (Note) item).noneMatch(item -> item.getId() == id)) {
deletedNotes.add(id);
}
}
for (final var id : deletedNotes) {
tracker.deselect(id);
}
}
});
mainViewModel.getSearchTerm().observe(this, adapter::setHighlightSearchQuery);
mainViewModel.getCategorySortingMethodOfSelectedCategory().observe(this, methodOfCategory -> {
updateSortMethodIcon(methodOfCategory.second);
activityBinding.sortingMethod.setOnClickListener((v) -> {
if (methodOfCategory.first != null) {
var newMethod = methodOfCategory.second;
if (newMethod == CategorySortingMethod.SORT_LEXICOGRAPHICAL_ASC) {
newMethod = CategorySortingMethod.SORT_MODIFIED_DESC;
} else {
newMethod = CategorySortingMethod.SORT_LEXICOGRAPHICAL_ASC;
}
final var modifyLiveData = mainViewModel.modifyCategoryOrder(methodOfCategory.first, newMethod);
modifyLiveData.observe(this, (next) -> modifyLiveData.removeObservers(this));
}
});
});
mainViewModel.getNavigationCategories().observe(this, navigationItems -> this.adapterCategories.setItems(navigationItems));
mainViewModel.getCurrentAccount().observe(this, (nextAccount) -> {
fabCreate.hide();
Glide.with(this).load(nextAccount.getUrl() + "/index.php/avatar/" + Uri.encode(nextAccount.getUserName()) + "/64").placeholder(R.drawable.ic_account_circle_grey_24dp).error(R.drawable.ic_account_circle_grey_24dp).apply(RequestOptions.circleCropTransform()).into(activityBinding.launchAccountSwitcher);
mainViewModel.synchronizeNotes(nextAccount, new IResponseCallback<>() {
@Override
public void onSuccess(Void v) {
Log.d(TAG, "Successfully synchronized notes for " + nextAccount.getAccountName());
}
@Override
public void onError(@NonNull Throwable t) {
runOnUiThread(() -> {
if (t instanceof IntendedOfflineException) {
Log.i(TAG, "Capabilities and notes not updated because " + nextAccount.getAccountName() + " is offline by intention.");
} else if (t instanceof NetworkErrorException) {
BrandedSnackbar.make(coordinatorLayout, getString(R.string.error_sync, getString(R.string.error_no_network)), Snackbar.LENGTH_LONG).show();
} else {
BrandedSnackbar.make(coordinatorLayout, R.string.error_synchronization, Snackbar.LENGTH_LONG).setAction(R.string.simple_more, v -> ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName())).show();
}
});
}
});
fabCreate.show();
activityBinding.launchAccountSwitcher.setOnClickListener((v) -> AccountSwitcherDialog.newInstance(nextAccount.getId()).show(getSupportFragmentManager(), AccountSwitcherDialog.class.getSimpleName()));
if (menuAdapter == null) {
menuAdapter = new MenuAdapter(getApplicationContext(), nextAccount, REQUEST_CODE_SERVER_SETTINGS, (menuItem) -> {
@Nullable Integer resultCode = menuItem.getResultCode();
if (resultCode == null) {
startActivity(menuItem.getIntent());
} else {
startActivityForResult(menuItem.getIntent(), resultCode);
}
});
binding.navigationMenu.setAdapter(menuAdapter);
} else {
menuAdapter.updateAccount(this, nextAccount);
}
});
}
use of it.niedermann.owncloud.notes.persistence.entity.Account in project nextcloud-notes by stefan-niedermann.
the class ManageAccountsActivity method onChangeNotesPath.
private void onChangeNotesPath(@NonNull Account localAccount) {
final var repository = NotesRepository.getInstance(getApplicationContext());
final var editText = new EditText(this);
final var wrapper = createDialogViewWrapper();
final var dialog = new BrandedAlertDialogBuilder(this).setTitle(R.string.settings_notes_path).setMessage(R.string.settings_notes_path_description).setView(wrapper).setNeutralButton(android.R.string.cancel, null).setPositiveButton(R.string.action_edit_save, (v, d) -> new Thread(() -> {
try {
final var putSettingsCall = repository.putServerSettings(AccountImporter.getSingleSignOnAccount(this, localAccount.getAccountName()), new NotesSettings(editText.getText().toString(), null), getPreferredApiVersion(localAccount.getApiVersion()));
putSettingsCall.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<NotesSettings> call, @NonNull Response<NotesSettings> response) {
final var body = response.body();
if (response.isSuccessful() && body != null) {
runOnUiThread(() -> Toast.makeText(ManageAccountsActivity.this, getString(R.string.settings_notes_path_success, body.getNotesPath()), Toast.LENGTH_LONG).show());
} else {
runOnUiThread(() -> Toast.makeText(ManageAccountsActivity.this, getString(R.string.http_status_code, response.code()), Toast.LENGTH_LONG).show());
}
}
@Override
public void onFailure(@NonNull Call<NotesSettings> call, @NonNull Throwable t) {
runOnUiThread(() -> ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()));
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
}).start()).show();
try {
repository.getServerSettings(AccountImporter.getSingleSignOnAccount(this, localAccount.getAccountName()), getPreferredApiVersion(localAccount.getApiVersion())).enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<NotesSettings> call, @NonNull Response<NotesSettings> response) {
runOnUiThread(() -> {
final var body = response.body();
if (response.isSuccessful() && body != null) {
wrapper.removeAllViews();
final var editText = new EditText(ManageAccountsActivity.this);
editText.setText(body.getNotesPath());
wrapper.addView(editText);
} else {
dialog.dismiss();
ExceptionDialogFragment.newInstance(new NetworkErrorException(getString(R.string.http_status_code, response.code()))).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
});
}
@Override
public void onFailure(@NonNull Call<NotesSettings> call, @NonNull Throwable t) {
runOnUiThread(() -> {
dialog.dismiss();
ExceptionDialogFragment.newInstance(t).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
});
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
dialog.dismiss();
ExceptionDialogFragment.newInstance(e).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
}
}
use of it.niedermann.owncloud.notes.persistence.entity.Account in project nextcloud-notes by stefan-niedermann.
the class MainViewModel method restoreInstanceState.
public void restoreInstanceState() {
Log.v(TAG, "[restoreInstanceState]");
final Account account = state.get(KEY_CURRENT_ACCOUNT);
if (account != null) {
postCurrentAccount(account);
}
postSearchTerm(state.get(KEY_SEARCH_TERM));
final NavigationCategory selectedCategory = state.get(KEY_SELECTED_CATEGORY);
if (selectedCategory != null) {
postSelectedCategory(selectedCategory);
Log.v(TAG, "[restoreInstanceState] - selectedCategory: " + selectedCategory);
}
postExpandedCategory(state.get(KEY_EXPANDED_CATEGORY));
}
use of it.niedermann.owncloud.notes.persistence.entity.Account in project nextcloud-notes by stefan-niedermann.
the class NotesRepository method addAccount.
// Accounts
@AnyThread
public LiveData<ImportStatus> addAccount(@NonNull String url, @NonNull String username, @NonNull String accountName, @NonNull Capabilities capabilities, @Nullable String displayName, @NonNull IResponseCallback<Account> callback) {
final var account = db.getAccountDao().getAccountById(db.getAccountDao().insert(new Account(url, username, accountName, displayName, capabilities)));
if (account == null) {
callback.onError(new Exception("Could not read created account."));
} else {
if (isSyncPossible()) {
syncActive.put(account.getId(), true);
try {
Log.d(TAG, "… starting now");
final NotesImportTask importTask = new NotesImportTask(context, this, account, importExecutor, apiProvider);
return importTask.importNotes(new IResponseCallback<>() {
@Override
public void onSuccess(Void result) {
callback.onSuccess(account);
}
@Override
public void onError(@NonNull Throwable t) {
callback.onError(t);
}
});
} catch (NextcloudFilesAppAccountNotFoundException e) {
Log.e(TAG, "… Could not find " + SingleSignOnAccount.class.getSimpleName() + " for account name " + account.getAccountName());
callback.onError(e);
}
} else {
callback.onError(new NetworkErrorException());
}
}
return new MutableLiveData<>(new ImportStatus());
}
use of it.niedermann.owncloud.notes.persistence.entity.Account in project nextcloud-notes by stefan-niedermann.
the class NoteListWidgetFactory method getViewAt.
@Override
public RemoteViews getViewAt(int position) {
final RemoteViews note_content;
if (position == 0) {
final Account localAccount = repo.getAccountById(data.getAccountId());
final Intent openIntent = new Intent(Intent.ACTION_MAIN).setComponent(new ComponentName(context.getPackageName(), MainActivity.class.getName()));
final Intent createIntent = new Intent(context, EditNoteActivity.class);
final Bundle extras = new Bundle();
extras.putSerializable(PARAM_CATEGORY, data.getMode() == MODE_DISPLAY_STARRED ? new NavigationCategory(ENavigationCategoryType.FAVORITES) : new NavigationCategory(localAccount.getId(), data.getCategory()));
extras.putLong(EditNoteActivity.PARAM_ACCOUNT_ID, data.getAccountId());
createIntent.putExtras(extras);
createIntent.setData(Uri.parse(createIntent.toUri(Intent.URI_INTENT_SCHEME)));
note_content = new RemoteViews(context.getPackageName(), R.layout.widget_entry_add);
note_content.setOnClickFillInIntent(R.id.widget_entry_content_tv, openIntent);
note_content.setOnClickFillInIntent(R.id.widget_entry_fav_icon, createIntent);
note_content.setTextViewText(R.id.widget_entry_content_tv, getCategoryTitle(context, data.getMode(), data.getCategory()));
note_content.setImageViewResource(R.id.widget_entry_fav_icon, R.drawable.ic_add_blue_24dp);
note_content.setInt(R.id.widget_entry_fav_icon, "setColorFilter", NotesColorUtil.contrastRatioIsSufficient(ContextCompat.getColor(context, R.color.widget_background), localAccount.getColor()) ? localAccount.getColor() : ContextCompat.getColor(context, R.color.widget_foreground));
} else {
position--;
if (position > dbNotes.size() - 1 || dbNotes.get(position) == null) {
Log.e(TAG, "Could not find position \"" + position + "\" in dbNotes list.");
return null;
}
final Note note = dbNotes.get(position);
final Intent fillInIntent = new Intent(context, EditNoteActivity.class);
final Bundle extras = new Bundle();
extras.putLong(EditNoteActivity.PARAM_NOTE_ID, note.getId());
extras.putLong(EditNoteActivity.PARAM_ACCOUNT_ID, note.getAccountId());
fillInIntent.putExtras(extras);
fillInIntent.setData(Uri.parse(fillInIntent.toUri(Intent.URI_INTENT_SCHEME)));
note_content = new RemoteViews(context.getPackageName(), R.layout.widget_entry);
note_content.setOnClickFillInIntent(R.id.widget_note_list_entry, fillInIntent);
note_content.setTextViewText(R.id.widget_entry_content_tv, note.getTitle());
note_content.setImageViewResource(R.id.widget_entry_fav_icon, note.getFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp);
}
return note_content;
}
Aggregations