Merge branch '814-normalize-database' into 603-sorting-method

This commit is contained in:
IF-ACT 2020-05-24 20:53:33 +08:00
commit 25dddd1dc8
31 changed files with 444 additions and 228 deletions

View file

@ -1,20 +0,0 @@
# SSO Announcment
Beginning with version `v2.0.0`, Notes for android will depend on Nextcloud Single-Sign-On.
## What do you need to do?
It is recommended, to perform a full synchronisation with the old version of the Notes app, before you upgrade.
You have likely installed an up-to-date version of the [Files app](https://play.google.com/store/apps/details?id=com.nextcloud.client). In this case, you will have nothing more to do.
In case you do not have it installed yet, you can get it for free from Play Store or F-Droid.
When you upgrade the Notes app, you will be asked to select an account (which you previously configured at the files app).
It is important that you **select** at the first run **the same account which you already were using** in the Notes app. This will make the first run smooth and make sure, that local edited, but not synced notes won't get lost.
## Benefits for you
- **:lock: Security benefits:** The notes app does no longer have to store a password itself.
- **:electric_plug: Reliability:** The complete network stack could be removed because all network activities are lead through the files app. This allows us to use e. g. the same stack for self signed certificates.
- **:bulb: Comfort:** You won't have to enter a server address, nor a username or a password. Just pick an existing account from the list.

View file

@ -13,8 +13,8 @@ android {
applicationId "it.niedermann.owncloud.notes"
minSdkVersion 16
targetSdkVersion 29
versionCode 2012000
versionName "2.12.0"
versionCode 2012001
versionName "2.12.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View file

@ -42,6 +42,7 @@ public class EditNoteActivity extends LockedActivity implements BaseNoteFragment
public static final String PARAM_ACCOUNT_ID = "accountId";
public static final String PARAM_CATEGORY = "category";
public static final String PARAM_CONTENT = "content";
public static final String PARAM_FAVORITE = "favorite";
private ActivityEditBinding binding;

View file

@ -3,20 +3,20 @@ package it.niedermann.owncloud.notes.android.activity;
import android.app.Activity;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.SQLException;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.widget.Toolbar;
import androidx.preference.PreferenceManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import it.niedermann.owncloud.notes.ExceptionHandler;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget;
import it.niedermann.owncloud.notes.model.DBNote;
import it.niedermann.owncloud.notes.model.Item;
import it.niedermann.owncloud.notes.model.SingleNoteWidgetData;
import it.niedermann.owncloud.notes.util.Notes;
public class SelectSingleNoteActivity extends NotesListViewActivity {
@ -43,29 +43,34 @@ public class SelectSingleNoteActivity extends NotesListViewActivity {
@Override
public void onNoteClick(int position, View v) {
Item item = adapter.getItem(position);
DBNote note = (DBNote) item;
long noteID = note.getId();
final DBNote note = (DBNote) adapter.getItem(position);
final Bundle extras = getIntent().getExtras();
if (extras == null) {
finish();
return;
}
assert extras != null;
int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
SharedPreferences.Editor sp = PreferenceManager.getDefaultSharedPreferences(this).edit();
sp.putLong(SingleNoteWidget.WIDGET_KEY + appWidgetId, noteID);
sp.putLong(SingleNoteWidget.ACCOUNT_ID_KEY + appWidgetId, note.getAccountId());
sp.putString(SingleNoteWidget.DARK_THEME_KEY + appWidgetId, Notes.getAppTheme(getApplicationContext()).name());
sp.apply();
Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null,
getApplicationContext(), SingleNoteWidget.class);
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
try {
db.createOrUpdateSingleNoteWidgetData(
new SingleNoteWidgetData(
appWidgetId,
note.getAccountId(),
note.getId(),
Notes.getAppTheme(this).getModeId()
)
);
final Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null,
getApplicationContext(), SingleNoteWidget.class)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, updateIntent);
getApplicationContext().sendBroadcast(updateIntent);
} catch (SQLException e) {
Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
finish();
}
}

View file

@ -17,8 +17,11 @@ import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.android.DarkModeSetting;
import it.niedermann.owncloud.notes.android.activity.EditNoteActivity;
import it.niedermann.owncloud.notes.android.activity.NotesListViewActivity;
import it.niedermann.owncloud.notes.model.Category;
import it.niedermann.owncloud.notes.util.Notes;
import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.PARAM_CATEGORY;
public class NoteListWidget extends AppWidgetProvider {
private static final String TAG = NoteListWidget.class.getSimpleName();
public static final String WIDGET_MODE_KEY = "NLW_mode";
@ -66,11 +69,11 @@ public class NoteListWidget extends AppWidgetProvider {
// Launch create note activity if user taps "+" icon on header
PendingIntent newNoteI = PendingIntent.getActivity(context, 0,
(new Intent(context, EditNoteActivity.class)),
(new Intent(context, EditNoteActivity.class).putExtra(PARAM_CATEGORY, new Category(category, displayMode == NLW_DISPLAY_STARRED))),
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent templatePI = PendingIntent.getActivity(context, 0,
(new Intent(context, EditNoteActivity.class)),
(new Intent(context, EditNoteActivity.class).putExtra(PARAM_CATEGORY, new Category(category, displayMode == NLW_DISPLAY_STARRED))),
PendingIntent.FLAG_UPDATE_CURRENT);
if (Notes.isDarkThemeActive(context, darkTheme)) {

View file

@ -8,36 +8,36 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;
import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import java.util.NoSuchElementException;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.android.DarkModeSetting;
import it.niedermann.owncloud.notes.android.activity.EditNoteActivity;
import it.niedermann.owncloud.notes.android.fragment.BaseNoteFragment;
import it.niedermann.owncloud.notes.model.SingleNoteWidgetData;
import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.util.Notes;
public class SingleNoteWidget extends AppWidgetProvider {
public static final String DARK_THEME_KEY = "SNW_darkTheme";
public static final String WIDGET_KEY = "single_note_widget";
public static final String ACCOUNT_ID_KEY = "SNW_accountId";
private static final String TAG = SingleNoteWidget.class.getSimpleName();
static void updateAppWidget(Context context, AppWidgetManager awm, int[] appWidgetIds) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
Intent templateIntent = new Intent(context, EditNoteActivity.class);
final Intent templateIntent = new Intent(context, EditNoteActivity.class);
final NotesDatabase db = NotesDatabase.getInstance(context);
for (int appWidgetId : appWidgetIds) {
// onUpdate has been triggered before the user finished configuring the widget
if ((sp.getLong(WIDGET_KEY + appWidgetId, -1)) == -1) {
return;
}
try {
final SingleNoteWidgetData data = db.getSingleNoteWidgetData(appWidgetId);
DarkModeSetting darkTheme = NoteWidgetHelper.getDarkThemeSetting(sp, DARK_THEME_KEY, appWidgetId);
templateIntent.putExtra(BaseNoteFragment.PARAM_ACCOUNT_ID, sp.getLong(ACCOUNT_ID_KEY + appWidgetId, -1));
templateIntent.putExtra(BaseNoteFragment.PARAM_ACCOUNT_ID, data.getAccountId());
PendingIntent templatePendingIntent = PendingIntent.getActivity(context, appWidgetId, templateIntent,
final PendingIntent templatePendingIntent = PendingIntent.getActivity(context, appWidgetId, templateIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
Intent serviceIntent = new Intent(context, SingleNoteWidgetService.class);
@ -46,7 +46,7 @@ public class SingleNoteWidget extends AppWidgetProvider {
RemoteViews views;
if (Notes.isDarkThemeActive(context, darkTheme)) {
if (Notes.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode()))) {
views = new RemoteViews(context.getPackageName(), R.layout.widget_single_note_dark);
views.setPendingIntentTemplate(R.id.single_note_widget_lv_dark, templatePendingIntent);
views.setRemoteAdapter(R.id.single_note_widget_lv_dark, serviceIntent);
@ -60,6 +60,9 @@ public class SingleNoteWidget extends AppWidgetProvider {
awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv);
}
awm.updateAppWidget(appWidgetId, views);
} catch (NoSuchElementException e) {
Log.i(TAG, "onUpdate has been triggered before the user finished configuring the widget");
}
}
}
@ -83,10 +86,10 @@ public class SingleNoteWidget extends AppWidgetProvider {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
final NotesDatabase db = NotesDatabase.getInstance(context);
for (int appWidgetId : appWidgetIds) {
editor.remove(WIDGET_KEY + appWidgetId);
editor.remove(DARK_THEME_KEY + appWidgetId);
editor.remove(ACCOUNT_ID_KEY + appWidgetId);
db.removeSingleNoteWidget(appWidgetId);
}
editor.apply();

View file

@ -3,25 +3,25 @@ package it.niedermann.owncloud.notes.android.appwidget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import androidx.preference.PreferenceManager;
import com.yydcdut.markdown.MarkdownProcessor;
import com.yydcdut.markdown.syntax.text.TextFactory;
import java.util.NoSuchElementException;
import it.niedermann.owncloud.notes.R;
import it.niedermann.owncloud.notes.android.DarkModeSetting;
import it.niedermann.owncloud.notes.android.activity.EditNoteActivity;
import it.niedermann.owncloud.notes.model.DBNote;
import it.niedermann.owncloud.notes.model.SingleNoteWidgetData;
import it.niedermann.owncloud.notes.persistence.NotesDatabase;
import it.niedermann.owncloud.notes.util.MarkDownUtil;
import it.niedermann.owncloud.notes.util.Notes;
import static it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget.DARK_THEME_KEY;
import static it.niedermann.owncloud.notes.util.MarkDownUtil.parseCompat;
public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFactory {
@ -32,8 +32,7 @@ public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFa
private NotesDatabase db;
private DBNote note;
private final SharedPreferences sp;
private boolean darkModeActive;
private boolean darkModeActive = false;
private static final String TAG = SingleNoteWidget.class.getSimpleName();
@ -41,32 +40,37 @@ public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFa
this.context = context;
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
sp = PreferenceManager.getDefaultSharedPreferences(this.context);
darkModeActive = Notes.isDarkThemeActive(context, NoteWidgetHelper.getDarkThemeSetting(sp, DARK_THEME_KEY, appWidgetId));
db = NotesDatabase.getInstance(context);
markdownProcessor = new MarkdownProcessor(this.context);
markdownProcessor.factory(TextFactory.create());
try {
SingleNoteWidgetData data = db.getSingleNoteWidgetData(appWidgetId);
darkModeActive = Notes.isDarkThemeActive(context, DarkModeSetting.fromModeID(data.getThemeMode()));
} catch (NoSuchElementException e) {
Log.w(TAG, "Widget with ID " + appWidgetId + " seems to be not configured yet.");
} finally {
markdownProcessor.config(MarkDownUtil.getMarkDownConfiguration(this.context, darkModeActive).build());
}
}
@Override
public void onCreate() {
db = NotesDatabase.getInstance(context);
}
}
@Override
public void onDataSetChanged() {
long noteID = sp.getLong(SingleNoteWidget.WIDGET_KEY + appWidgetId, -1);
if (noteID >= 0) {
Log.v(TAG, "Fetch note for account " + SingleNoteWidget.ACCOUNT_ID_KEY + appWidgetId);
Log.v(TAG, "Fetch note for account " + sp.getLong(SingleNoteWidget.ACCOUNT_ID_KEY + appWidgetId, -1));
Log.v(TAG, "Fetch note with id " + noteID);
note = db.getNote(sp.getLong(SingleNoteWidget.ACCOUNT_ID_KEY + appWidgetId, -1), noteID);
try {
final SingleNoteWidgetData data = db.getSingleNoteWidgetData(appWidgetId);
final long noteId = data.getNoteId();
Log.v(TAG, "Fetch note with id " + noteId);
note = db.getNote(data.getAccountId(), noteId);
if (note == null) {
Log.e(TAG, "Error: note not found");
}
} catch (NoSuchElementException e) {
Log.w(TAG, "Widget with ID " + appWidgetId + " seems to be not configured yet.");
}
}

View file

@ -11,12 +11,6 @@ public enum DBStatus {
*/
VOID(""),
/**
* LOCAL_CREATED is not used anymore, since a newly created note has REMOTE_ID=0
*/
@Deprecated
LOCAL_CREATED("LOCAL_CREATED"),
/**
* LOCAL_EDITED means that a Note was created and/or changed since the last successful synchronization.
* If it was newly created, then REMOTE_ID is 0

View file

@ -0,0 +1,51 @@
package it.niedermann.owncloud.notes.model;
public class SingleNoteWidgetData {
private int appWidgetId;
private long accountId;
private long noteId;
private int themeMode;
public SingleNoteWidgetData() {
}
public SingleNoteWidgetData(int appWidgetId, long accountId, long noteId, int themeMode) {
this.appWidgetId = appWidgetId;
this.accountId = accountId;
this.noteId = noteId;
this.themeMode = themeMode;
}
public int getAppWidgetId() {
return appWidgetId;
}
public void setAppWidgetId(int appWidgetId) {
this.appWidgetId = appWidgetId;
}
public long getAccountId() {
return accountId;
}
public void setAccountId(long accountId) {
this.accountId = accountId;
}
public long getNoteId() {
return noteId;
}
public void setNoteId(long noteId) {
this.noteId = noteId;
}
public int getThemeMode() {
return themeMode;
}
public void setThemeMode(int themeMode) {
this.themeMode = themeMode;
}
}

View file

@ -40,14 +40,15 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
protected static final String table_notes = "NOTES";
protected static final String table_accounts = "ACCOUNTS";
protected static final String table_category = "CATEGORIES";
protected static final String table_widget_single_notes = "WIDGET_SINGLE_NOTES";
protected static final String key_id = "ID";
protected static final String key_url = "URL";
protected static final String key_account_name = "ACCOUNT_NAME";
protected static final String key_username = "USERNAME";
protected static final String key_account_id = "ACCOUNT_ID";
protected static final String key_note_id = "NOTE_ID";
protected static final String key_remote_id = "REMOTEID";
protected static final String key_status = "STATUS";
protected static final String key_title = "TITLE";
@ -64,6 +65,7 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
protected static final String key_category_id = "CATEGORY_ID";
protected static final String key_category_title = "CATEGORY_TITLE";
protected static final String key_category_account_id = "CATEGORY_ACCOUNT_ID";
protected static final String key_theme_mode = "THEME_MODE";
protected static final String key_category_sorting_method = "CATEGORY_SORTING_METHOD";
@ -88,13 +90,14 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
createAccountTable(db);
createNotesTable(db);
createCategoryTable(db);
createWidgetSingleNoteTable(db);
}
private void createNotesTable(@NonNull SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + table_notes + " ( " +
key_id + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
key_remote_id + " INTEGER, " +
key_account_id + " INTEGER, " +
key_account_id + " INTEGER, " + // TODO NOT NULL
key_status + " VARCHAR(50), " +
key_title + " TEXT, " +
key_modified + " INTEGER DEFAULT 0, " +
@ -105,7 +108,7 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
key_excerpt + " TEXT NOT NULL DEFAULT '', " +
"FOREIGN KEY(" + key_category + ") REFERENCES " + table_category + "(" + key_category_id + "), " +
"FOREIGN KEY(" + key_account_id + ") REFERENCES " + table_accounts + "(" + key_id + "))");
createNotesIndexes(db);
DatabaseIndexUtil.createIndex(db, table_notes, key_remote_id, key_account_id, key_status, key_favorite, key_category, key_modified);
}
private void createAccountTable(@NonNull SQLiteDatabase db) {
@ -119,82 +122,85 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
key_api_version + " TEXT, " +
key_color + " VARCHAR(6) NOT NULL DEFAULT '000000', " +
key_text_color + " VARCHAR(6) NOT NULL DEFAULT '0082C9', " +
key_capabilities_etag + " TEXT, " +
" FOREIGN KEY(" + key_id + ") REFERENCES " + table_category + "(" + key_category_account_id + "));");
createAccountIndexes(db);
key_capabilities_etag + " TEXT);");
DatabaseIndexUtil.createIndex(db, table_accounts, key_url, key_username, key_account_name, key_etag, key_modified);
}
private void createCategoryTable(@NonNull SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + table_category + "(" +
key_category_id + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
key_category_account_id + " INTEGER, " +
key_category_title + " TEXT, " +
key_category_account_id + " INTEGER NOT NULL, " +
key_category_title + " TEXT NOT NULL, " +
key_category_sorting_method + " INTEGER DEFAULT 0, " +
" UNIQUE( " + key_category_account_id + " , " + key_category_title + "))");
createCategoryIndexes(db);
" UNIQUE( " + key_category_account_id + " , " + key_category_title + "), " +
" FOREIGN KEY(" + key_category_account_id + ") REFERENCES " + table_accounts + "(" + key_id + "));");
DatabaseIndexUtil.createIndex(db, table_category, key_category_id, key_category_account_id, key_category_title, key_category_sorting_method);
}
@SuppressWarnings("deprecation")
private void createWidgetSingleNoteTable(@NonNull SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + table_widget_single_notes + " ( " +
key_id + " INTEGER PRIMARY KEY, " +
key_account_id + " INTEGER, " +
key_note_id + " INTEGER, " +
key_theme_mode + " INTEGER NOT NULL, " +
"FOREIGN KEY(" + key_account_id + ") REFERENCES " + table_accounts + "(" + key_id + "), " +
"FOREIGN KEY(" + key_note_id + ") REFERENCES " + table_notes + "(" + key_id + "))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 3) {
if (oldVersion < 4) {
recreateDatabase(db);
return;
}
if (oldVersion < 4) {
db.delete(table_notes, null, null);
db.delete(table_accounts, null, null);
}
if (oldVersion < 5) {
db.execSQL("ALTER TABLE " + table_notes + " ADD COLUMN " + key_remote_id + " INTEGER");
db.execSQL("UPDATE " + table_notes + " SET " + key_remote_id + "=" + key_id + " WHERE (" + key_remote_id + " IS NULL OR " + key_remote_id + "=0) AND " + key_status + "!=?", new String[]{DBStatus.LOCAL_CREATED.getTitle()});
db.execSQL("UPDATE " + table_notes + " SET " + key_remote_id + "=0, " + key_status + "=? WHERE " + key_status + "=?", new String[]{DBStatus.LOCAL_EDITED.getTitle(), DBStatus.LOCAL_CREATED.getTitle()});
db.execSQL("ALTER TABLE NOTES ADD COLUMN REMOTEID INTEGER");
db.execSQL("UPDATE NOTES SET REMOTEID=ID WHERE (REMOTEID IS NULL OR REMOTEID=0) AND STATUS!=?", new String[]{"LOCAL_CREATED"});
db.execSQL("UPDATE NOTES SET REMOTEID=0, STATUS=? WHERE STATUS=?", new String[]{DBStatus.LOCAL_EDITED.getTitle(), "LOCAL_CREATED"});
}
if (oldVersion < 6) {
db.execSQL("ALTER TABLE " + table_notes + " ADD COLUMN " + key_favorite + " INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE NOTES ADD COLUMN FAVORITE INTEGER DEFAULT 0");
}
if (oldVersion < 7) {
DatabaseIndexUtil.dropIndexes(db);
db.execSQL("ALTER TABLE " + table_notes + " ADD COLUMN " + key_category + " TEXT NOT NULL DEFAULT ''");
db.execSQL("ALTER TABLE " + table_notes + " ADD COLUMN " + key_etag + " TEXT");
DatabaseIndexUtil.createIndex(db, table_notes, key_remote_id, key_status, key_favorite, key_category, key_modified);
db.execSQL("ALTER TABLE NOTES ADD COLUMN CATEGORY TEXT NOT NULL DEFAULT ''");
db.execSQL("ALTER TABLE NOTES ADD COLUMN ETAG TEXT");
DatabaseIndexUtil.createIndex(db, "NOTES", "REMOTEID", "STATUS", "FAVORITE", "CATEGORY", "MODIFIED");
}
if (oldVersion < 8) {
final String table_temp = "NOTES_TEMP";
db.execSQL("CREATE TABLE " + table_temp + " ( " +
key_id + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
key_remote_id + " INTEGER, " +
key_status + " VARCHAR(50), " +
key_title + " TEXT, " +
key_modified + " INTEGER DEFAULT 0, " +
key_content + " TEXT, " +
key_favorite + " INTEGER DEFAULT 0, " +
key_category + " TEXT NOT NULL DEFAULT '', " +
key_etag + " TEXT)");
DatabaseIndexUtil.createIndex(db, table_temp, key_remote_id, key_status, key_favorite, key_category, key_modified);
db.execSQL(String.format("INSERT INTO %s(%s,%s,%s,%s,%s,%s,%s,%s,%s) ", table_temp, key_id, key_remote_id, key_status, key_title, key_modified, key_content, key_favorite, key_category, key_etag)
+ String.format("SELECT %s,%s,%s,%s,strftime('%%s',%s),%s,%s,%s,%s FROM %s", key_id, key_remote_id, key_status, key_title, key_modified, key_content, key_favorite, key_category, key_etag, table_notes));
db.execSQL(String.format("DROP TABLE %s", table_notes));
db.execSQL(String.format("ALTER TABLE %s RENAME TO %s", table_temp, table_notes));
"ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"REMOTEID INTEGER, " +
"STATUS VARCHAR(50), " +
"TITLE TEXT, " +
"MODIFIED INTEGER DEFAULT 0, " +
"CONTENT TEXT, " +
"FAVORITE INTEGER DEFAULT 0, " +
"CATEGORY TEXT NOT NULL DEFAULT '', " +
"ETAG TEXT)");
DatabaseIndexUtil.createIndex(db, table_temp, "REMOTEID", "STATUS", "FAVORITE", "CATEGORY", "MODIFIED");
db.execSQL(String.format("INSERT INTO %s(%s,%s,%s,%s,%s,%s,%s,%s,%s) ", table_temp, "ID", "REMOTEID", "STATUS", "TITLE", "MODIFIED", "CONTENT", "FAVORITE", "CATEGORY", "ETAG")
+ String.format("SELECT %s,%s,%s,%s,strftime('%%s',%s),%s,%s,%s,%s FROM %s", "ID", "REMOTEID", "STATUS", "TITLE", "MODIFIED", "CONTENT", "FAVORITE", "CATEGORY", "ETAG", "NOTES"));
db.execSQL("DROP TABLE NOTES");
db.execSQL(String.format("ALTER TABLE %s RENAME TO %s", table_temp, "NOTES"));
}
if (oldVersion < 9) {
// Create accounts table
db.execSQL("CREATE TABLE " + table_accounts + " ( " +
key_id + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
key_url + " TEXT, " +
key_username + " TEXT, " +
key_account_name + " TEXT UNIQUE, " +
key_etag + " TEXT, " +
key_modified + " INTEGER, " +
key_color + " VARCHAR(6) NOT NULL DEFAULT '000000', " +
key_text_color + " VARCHAR(6) NOT NULL DEFAULT '0082C9', " +
key_capabilities_etag + " TEXT)");
createAccountIndexes(db);
db.execSQL("CREATE TABLE ACCOUNTS ( " +
"ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"URL TEXT, " +
"USERNAME TEXT, " +
"ACCOUNT_NAME TEXT UNIQUE, " +
"ETAG TEXT, " +
"MODIFIED INTEGER)");
DatabaseIndexUtil.createIndex(db, "ACCOUNTS", "URL", "USERNAME", "ACCOUNT_NAME", "ETAG", "MODIFIED");
// Add accountId to notes table
db.execSQL("ALTER TABLE " + table_notes + " ADD COLUMN " + key_account_id + " INTEGER NOT NULL DEFAULT 0");
DatabaseIndexUtil.createIndex(db, table_notes, key_account_id);
db.execSQL("ALTER TABLE NOTES ADD COLUMN ACCOUNT_ID INTEGER NOT NULL DEFAULT 0");
DatabaseIndexUtil.createIndex(db, "NOTES", "ACCOUNT_ID");
// Migrate existing account from SharedPreferences
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
@ -206,36 +212,36 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
String accountName = username + "@" + new URL(url).getHost();
ContentValues migratedAccountValues = new ContentValues();
migratedAccountValues.put(key_url, url);
migratedAccountValues.put(key_username, username);
migratedAccountValues.put(key_account_name, accountName);
db.insert(table_accounts, null, migratedAccountValues);
migratedAccountValues.put("URL", url);
migratedAccountValues.put("USERNAME", username);
migratedAccountValues.put("ACCOUNT_NAME", accountName);
db.insert("ACCOUNTS", null, migratedAccountValues);
// After successful insertion of migrated account, set accountId to 1 in each note
ContentValues values = new ContentValues();
values.put(key_account_id, 1);
db.update(table_notes, values, key_account_id + " = ?", new String[]{"NULL"});
values.put("ACCOUNT_ID", 1);
db.update("NOTES", values, "ACCOUNT_ID = ?", new String[]{"NULL"});
// Add FOREIGN_KEY constraint
final String table_temp = "NOTES_TEMP";
db.execSQL(String.format("ALTER TABLE %s RENAME TO %s", table_notes, table_temp));
db.execSQL(String.format("ALTER TABLE %s RENAME TO %s", "NOTES", table_temp));
db.execSQL("CREATE TABLE " + table_notes + " ( " +
key_id + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
key_remote_id + " INTEGER, " +
key_account_id + " INTEGER, " +
key_status + " VARCHAR(50), " +
key_title + " TEXT, " +
key_modified + " INTEGER DEFAULT 0, " +
key_content + " TEXT, " +
key_favorite + " INTEGER DEFAULT 0, " +
key_category + " TEXT NOT NULL DEFAULT '', " +
key_etag + " TEXT," +
"FOREIGN KEY(" + key_account_id + ") REFERENCES " + table_accounts + "(" + key_id + "))");
DatabaseIndexUtil.createIndex(db, table_notes, key_remote_id, key_account_id, key_status, key_favorite, key_category, key_modified);
db.execSQL("CREATE TABLE NOTES ( " +
"ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"REMOTEID INTEGER, " +
"ACCOUNT_ID INTEGER, " +
"STATUS VARCHAR(50), " +
"TITLE TEXT, " +
"MODIFIED INTEGER DEFAULT 0, " +
"CONTENT TEXT, " +
"FAVORITE INTEGER DEFAULT 0, " +
"CATEGORY TEXT NOT NULL DEFAULT '', " +
"ETAG TEXT," +
"FOREIGN KEY(ACCOUNT_ID) REFERENCES ACCOUNTS(ID))");
DatabaseIndexUtil.createIndex(db, "NOTES", "REMOTEID", "ACCOUNT_ID", "STATUS", "FAVORITE", "CATEGORY", "MODIFIED");
db.execSQL(String.format("INSERT INTO %s(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ", table_notes, key_id, key_account_id, key_remote_id, key_status, key_title, key_modified, key_content, key_favorite, key_category, key_etag)
+ String.format("SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s", key_id, values.get(key_account_id), key_remote_id, key_status, key_title, key_modified, key_content, key_favorite, key_category, key_etag, table_temp));
db.execSQL(String.format("INSERT INTO %s(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) ", "NOTES", "ID", "ACCOUNT_ID", "REMOTEID", "STATUS", "TITLE", "MODIFIED", "CONTENT", "FAVORITE", "CATEGORY", "ETAG")
+ String.format("SELECT %s,%s,%s,%s,%s,%s,%s,%s,%s,%s FROM %s", "ID", values.get("ACCOUNT_ID"), "REMOTEID", "STATUS", "TITLE", "MODIFIED", "CONTENT", "FAVORITE", "CATEGORY", "ETAG", table_temp));
db.execSQL(String.format("DROP TABLE %s;", table_temp));
AppWidgetManager awm = AppWidgetManager.getInstance(context);
@ -252,8 +258,8 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
}
for (int appWidgetId : appWidgetIdsSNW) {
if (sharedPreferences.getLong(SingleNoteWidget.WIDGET_KEY + appWidgetId, -1) >= 0) {
editor.putLong(SingleNoteWidget.ACCOUNT_ID_KEY + appWidgetId, 1);
if (sharedPreferences.getLong("single_note_widget" + appWidgetId, -1) >= 0) {
editor.putLong("SNW_accountId" + appWidgetId, 1);
}
}
@ -279,12 +285,12 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
}
}
if (oldVersion < 10) {
db.execSQL("ALTER TABLE " + table_notes + " ADD COLUMN " + key_excerpt + " INTEGER NOT NULL DEFAULT ''");
Cursor cursor = db.query(table_notes, new String[]{key_id, key_content}, null, null, null, null, null, null);
db.execSQL("ALTER TABLE NOTES ADD COLUMN EXCERPT INTEGER NOT NULL DEFAULT ''");
Cursor cursor = db.query("NOTES", new String[]{"ID", "CONTENT"}, null, null, null, null, null, null);
while (cursor.moveToNext()) {
ContentValues values = new ContentValues();
values.put(key_excerpt, NoteUtil.generateNoteExcerpt(cursor.getString(1)));
db.update(table_notes, values, key_id + " = ? ", new String[]{cursor.getString(0)});
values.put("EXCERPT", NoteUtil.generateNoteExcerpt(cursor.getString(1)));
db.update("NOTES", values, "ID" + " = ? ", new String[]{cursor.getString(0)});
}
cursor.close();
}
@ -294,7 +300,7 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
Map<String, ?> prefs = sharedPreferences.getAll();
for (Map.Entry<String, ?> pref : prefs.entrySet()) {
String key = pref.getKey();
if ("darkTheme".equals(key) || key.startsWith(NoteListWidget.DARK_THEME_KEY) || key.startsWith(SingleNoteWidget.DARK_THEME_KEY)) {
if ("darkTheme".equals(key) || key.startsWith(NoteListWidget.DARK_THEME_KEY) || key.startsWith("SNW_darkTheme")) {
Boolean darkTheme = (Boolean) pref.getValue();
editor.putString(pref.getKey(), darkTheme ? DarkModeSetting.DARK.name() : DarkModeSetting.LIGHT.name());
}
@ -302,22 +308,97 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
editor.apply();
}
if (oldVersion < 12) {
db.execSQL("ALTER TABLE " + table_accounts + " ADD COLUMN " + key_api_version + " TEXT");
db.execSQL("ALTER TABLE " + table_accounts + " ADD COLUMN " + key_color + " VARCHAR(6) NOT NULL DEFAULT '000000'");
db.execSQL("ALTER TABLE " + table_accounts + " ADD COLUMN " + key_text_color + " VARCHAR(6) NOT NULL DEFAULT '0082C9'");
db.execSQL("ALTER TABLE ACCOUNTS ADD COLUMN API_VERSION TEXT");
db.execSQL("ALTER TABLE ACCOUNTS ADD COLUMN COLOR VARCHAR(6) NOT NULL DEFAULT '000000'");
db.execSQL("ALTER TABLE ACCOUNTS ADD COLUMN TEXT_COLOR VARCHAR(6) NOT NULL DEFAULT '0082C9'");
CapabilitiesWorker.update(context);
}
if (oldVersion < 13) {
db.execSQL("ALTER TABLE " + table_accounts + " ADD COLUMN " + key_capabilities_etag + " TEXT");
db.execSQL("ALTER TABLE ACCOUNTS ADD COLUMN CAPABILITIES_ETAG TEXT");
WorkManager.getInstance(context.getApplicationContext()).cancelUniqueWork("it.niedermann.owncloud.notes.persistence.SyncWorker");
WorkManager.getInstance(context.getApplicationContext()).cancelUniqueWork("SyncWorker");
}
if (oldVersion < 14) {
// #754 Move single note widget preferences to database
db.execSQL("CREATE TABLE WIDGET_SINGLE_NOTES ( " +
"ID INTEGER PRIMARY KEY, " +
"ACCOUNT_ID INTEGER, " +
"NOTE_ID INTEGER, " +
"THEME_MODE INTEGER NOT NULL, " +
"FOREIGN KEY(ACCOUNT_ID) REFERENCES ACCOUNTS(ID), " +
"FOREIGN KEY(NOTE_ID) REFERENCES NOTES(ID))");
final String SP_WIDGET_KEY = "single_note_widget";
final String SP_ACCOUNT_ID_KEY = "SNW_accountId";
final String SP_DARK_THEME_KEY = "SNW_darkTheme";
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
Map<String, ?> prefs = sharedPreferences.getAll();
for (Map.Entry<String, ?> pref : prefs.entrySet()) {
final String key = pref.getKey();
Integer widgetId = null;
Long noteId = null;
Long accountId = null;
Integer themeMode = null;
if (key != null && key.startsWith(SP_WIDGET_KEY)) {
try {
widgetId = Integer.parseInt(key.substring(SP_WIDGET_KEY.length()));
noteId = (Long) pref.getValue();
accountId = sharedPreferences.getLong(SP_ACCOUNT_ID_KEY + widgetId, -1);
try {
themeMode = DarkModeSetting.valueOf(sharedPreferences.getString(SP_DARK_THEME_KEY + widgetId, DarkModeSetting.SYSTEM_DEFAULT.name())).getModeId();
} catch (ClassCastException e) {
//DARK_THEME was a boolean in older versions of the app. We thereofre have to still support the old setting.
themeMode = sharedPreferences.getBoolean(SP_DARK_THEME_KEY + widgetId, false) ? DarkModeSetting.DARK.getModeId() : DarkModeSetting.LIGHT.getModeId();
}
ContentValues migratedWidgetValues = new ContentValues();
migratedWidgetValues.put("ID", widgetId);
migratedWidgetValues.put("ACCOUNT_ID", accountId);
migratedWidgetValues.put("NOTE_ID", noteId);
migratedWidgetValues.put("THEME_MODE", themeMode);
db.insert("WIDGET_SINGLE_NOTES", null, migratedWidgetValues);
} catch (Throwable t) {
Log.e(TAG, "Could not migrate widget {widgetId: " + widgetId + ", accountId: " + accountId + ", noteId: " + noteId + ", themeMode: " + themeMode + "}");
t.printStackTrace();
} finally {
// Clean up old shared preferences
editor.remove(SP_WIDGET_KEY + widgetId);
editor.remove(SP_DARK_THEME_KEY + widgetId);
editor.remove(SP_ACCOUNT_ID_KEY + widgetId);
}
}
}
editor.apply();
notifyNotesChanged();
}
if (oldVersion < 15) {
// Rename a tmp_NOTES table.
String tmpTableNotes = String.format("tmp_%s", table_notes);
db.execSQL("ALTER TABLE " + table_notes + " RENAME TO " + tmpTableNotes);
createNotesTable(db);
createCategoryTable(db);
String tmpTableNotes = String.format("tmp_%s", "NOTES");
db.execSQL("ALTER TABLE NOTES RENAME TO " + tmpTableNotes);
db.execSQL("CREATE TABLE NOTES ( " +
"ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"REMOTEID INTEGER, " +
"ACCOUNT_ID INTEGER, " +
"STATUS VARCHAR(50), " +
"TITLE TEXT, " +
"MODIFIED INTEGER DEFAULT 0, " +
"CONTENT TEXT, " +
"FAVORITE INTEGER DEFAULT 0, " +
"CATEGORY INTEGER, " +
"ETAG TEXT," +
"EXCERPT TEXT NOT NULL DEFAULT '', " +
"FOREIGN KEY(CATEGORY) REFERENCES CATEGORIES(CATEGORY_ID), " +
"FOREIGN KEY(ACCOUNT_ID) REFERENCES ACCOUNTS(ID))");
DatabaseIndexUtil.createIndex(db, "NOTES", "REMOTEID", "ACCOUNT_ID", "STATUS", "FAVORITE", "CATEGORY", "MODIFIED");
db.execSQL("CREATE TABLE CATEGORIES(" +
"CATEGORY_ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
"CATEGORY_ACCOUNT_ID INTEGER NOT NULL, " +
"CATEGORY_TITLE TEXT NOT NULL, " +
"UNIQUE( CATEGORY_ACCOUNT_ID , CATEGORY_TITLE), " +
"FOREIGN KEY(CATEGORY_ACCOUNT_ID) REFERENCES ACCOUNTS(ID))");
DatabaseIndexUtil.createIndex(db, "CATEGORIES", "CATEGORY_ID", "CATEGORY_ACCOUNT_ID", "CATEGORY_TITLE");
// A hashtable storing categoryTitle - categoryId Mapping
// This is used to prevent too many searches in database
Hashtable<String, Integer> categoryTitleIdMap = new Hashtable<>();
@ -326,38 +407,37 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
while (tmpNotesCursor.moveToNext()) {
String categoryTitle = tmpNotesCursor.getString(8);
int accountId = tmpNotesCursor.getInt(2);
int categoryId = 0;
Log.e("###", accountId + "");
Integer categoryId;
if (categoryTitleIdMap.containsKey(categoryTitle) && categoryTitleIdMap.get(categoryTitle) != null) {
categoryId = categoryTitleIdMap.get(categoryTitle);
} else {
// The category does not exists in the database, create it.
categoryId = id++;
ContentValues values = new ContentValues();
values.put(key_category_id, categoryId);
values.put(key_category_account_id, accountId);
values.put(key_category_title, categoryTitle);
db.insert(table_category, null, values);
values.put("CATEGORY_ID", categoryId);
values.put("CATEGORY_ACCOUNT_ID", accountId);
values.put("CATEGORY_TITLE", categoryTitle);
db.insert("CATEGORIES", null, values);
categoryTitleIdMap.put(categoryTitle, categoryId);
}
// Move the data in tmp_NOTES to NOTES
ContentValues values = new ContentValues();
values.put(key_id, tmpNotesCursor.getInt(0));
values.put(key_remote_id, tmpNotesCursor.getInt(1));
values.put(key_account_id, tmpNotesCursor.getInt(2));
values.put(key_status, tmpNotesCursor.getString(3));
values.put(key_title, tmpNotesCursor.getString(4));
values.put(key_modified, tmpNotesCursor.getLong(5));
values.put(key_content, tmpNotesCursor.getString(6));
values.put(key_favorite, tmpNotesCursor.getInt(7));
values.put(key_category, categoryId);
values.put(key_etag, tmpNotesCursor.getString(9));
values.put(key_etag, tmpNotesCursor.getString(10));
db.insert(table_notes, null, values);
values.put("ID", tmpNotesCursor.getInt(0));
values.put("REMOTEID", tmpNotesCursor.getInt(1));
values.put("ACCOUNT_ID", tmpNotesCursor.getInt(2));
values.put("STATUS", tmpNotesCursor.getString(3));
values.put("TITLE", tmpNotesCursor.getString(4));
values.put("MODIFIED", tmpNotesCursor.getLong(5));
values.put("CONTENT", tmpNotesCursor.getString(6));
values.put("FAVORITE", tmpNotesCursor.getInt(7));
values.put("CATEGORY", categoryId);
values.put("ETAG", tmpNotesCursor.getString(9));
values.put("EXCERPT", tmpNotesCursor.getString(10));
db.insert("NOTES", null, values);
}
tmpNotesCursor.close();
db.execSQL("DROP TABLE IF EXISTS " + tmpTableNotes);
createCategoryIndexes(db);
createNotesIndexes(db);
}
if (oldVersion < 15) {
// add a new column to store the sorting method for a category note list
@ -378,17 +458,6 @@ abstract class AbstractNotesDatabase extends SQLiteOpenHelper {
onCreate(db);
}
private static void createNotesIndexes(@NonNull SQLiteDatabase db) {
DatabaseIndexUtil.createIndex(db, table_notes, key_remote_id, key_account_id, key_status, key_favorite, key_category, key_modified);
}
private static void createAccountIndexes(@NonNull SQLiteDatabase db) {
DatabaseIndexUtil.createIndex(db, table_accounts, key_url, key_username, key_account_name, key_etag, key_modified);
}
private static void createCategoryIndexes(@NonNull SQLiteDatabase db) {
DatabaseIndexUtil.createIndex(db, table_category, key_category_id, key_category_account_id, key_category_title);
}
protected abstract void notifyNotesChanged();
}

View file

@ -9,6 +9,7 @@ import android.content.pm.ShortcutManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
@ -37,6 +38,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import it.niedermann.owncloud.notes.R;
@ -53,6 +55,7 @@ import it.niedermann.owncloud.notes.model.ISyncCallback;
import it.niedermann.owncloud.notes.model.LocalAccount;
import it.niedermann.owncloud.notes.model.NavigationAdapter;
import it.niedermann.owncloud.notes.util.CategorySortingMethod;
import it.niedermann.owncloud.notes.model.SingleNoteWidgetData;
import it.niedermann.owncloud.notes.util.NoteUtil;
import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT;
@ -565,7 +568,7 @@ public class NotesDatabase extends AbstractNotesDatabase {
values.put(key_modified, remoteNote.getModified().getTimeInMillis() / 1000);
values.put(key_content, remoteNote.getContent());
values.put(key_favorite, remoteNote.isFavorite());
values.put(key_category, getCategoryIdByTitle(id, remoteNote.getCategory()));
values.put(key_category, getCategoryIdByTitle(localAccount.getId(), remoteNote.getCategory()));
values.put(key_etag, remoteNote.getEtag());
values.put(key_excerpt, NoteUtil.generateNoteExcerpt(remoteNote.getContent()));
String whereClause;
@ -921,6 +924,44 @@ public class NotesDatabase extends AbstractNotesDatabase {
}
}
/**
* @param appWidgetId the id of the widget
* @return {@link SingleNoteWidgetData}
* @throws NoSuchElementException in case there is no {@link SingleNoteWidgetData} for the given appWidgetId
*/
@NonNull
public SingleNoteWidgetData getSingleNoteWidgetData(int appWidgetId) throws NoSuchElementException {
SingleNoteWidgetData data = new SingleNoteWidgetData();
final SQLiteDatabase db = getReadableDatabase();
final Cursor cursor = db.query(table_widget_single_notes, new String[]{key_account_id, key_note_id, key_theme_mode}, key_id + " = ?", new String[]{String.valueOf(appWidgetId)}, null, null, null, null);
if (cursor.moveToNext()) {
data.setAppWidgetId(appWidgetId);
data.setAccountId(cursor.getLong(0));
data.setNoteId(cursor.getLong(1));
data.setThemeMode(cursor.getInt(2));
} else {
throw new NoSuchElementException();
}
cursor.close();
return data;
}
public void removeSingleNoteWidget(int appWidgetId) {
final SQLiteDatabase db = getWritableDatabase();
db.delete(table_widget_single_notes, key_id + " = ?", new String[]{String.valueOf(appWidgetId)});
}
public void createOrUpdateSingleNoteWidgetData(@NonNull SingleNoteWidgetData data) throws SQLException {
validateAccountId(data.getAccountId());
final SQLiteDatabase db = getWritableDatabase();
final ContentValues values = new ContentValues();
values.put(key_id, data.getAppWidgetId());
values.put(key_account_id, data.getAccountId());
values.put(key_note_id, data.getNoteId());
values.put(key_theme_mode, data.getThemeMode());
db.replaceOrThrow(table_widget_single_notes, null, values);
}
private static void validateAccountId(long accountId) {
if (accountId < 1) {
throw new IllegalArgumentException("accountId must be greater than 0");

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Na vámi využívané instanci Nexcloud nezbývá žádné volné místo na úložišti. Aby bylo možné synchronizovat vaše místní změny do vámi využívaného cloudu, je třeba nejprve smazat nějaké soubory.</string>
<string name="error_dialog_contact_us">Pokud problémy přetrvávají, neváhejte se na nás obrátit. Kontaktní údaje naleznete v sekci „O aplikaci“ v postranním panelu.</string>
<string name="error_dialog_we_need_info">Abychom vám mohli pomoci, potřebujeme následující technické údaje:</string>
<string name="error_dialog_server_app_enabled">Ověřte, že na vámi využívaném serveru je nainstalovaná a zapnutá aplikace „Poznámky“.</string>
<string name="error_dialog_redirect">Vámi využívaný server odpověděl HTTP stavovým kódem 302, což ukazuje na to, že na serveru buď není nainstalovaná aplikace Poznámky, nebo je něco nesprávně nastavené. Toto může být způsobeno uživatelsky určenými přepsáními v souboru .htaccess nebo Nextcloud aplikacemi jako je OID klient.</string>
<string name="added_content">Přidáno „%1$s“</string>
<string name="shared_text_empty">Sdílený text je prázdný</string>
<string name="append_to_note">Připojit k poznámce</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Ihre Nextcloud-Installation hat keinen freien Speicherplatz mehr. Bitte löschen Sie einige Dateien, um Ihre lokalen Änderungen mit Ihrer Cloud synchronisieren zu können.</string>
<string name="error_dialog_contact_us">Bitte zögern Sie nicht, uns zu kontaktieren, wenn das Problem weiterhin besteht. Sie finden unsere Kontaktinformationen im Abschnitt \"Info\" in der Seitenleiste.</string>
<string name="error_dialog_we_need_info">Wir benötigen die folgenden technischen Informationen, um Ihnen helfen zu können:</string>
<string name="error_dialog_redirect">Ihr Server hat mit einem HTTP 302-Statuscode geantwortet, was bedeutet, dass Sie die Notes-App nicht auf Ihrem Server installiert haben oder etwas falsch konfiguriert ist. Dies kann durch benutzerdefinierte Überschreibungen in einer .htaccess-Datei oder durch Nextcloud-Apps wie OID-Client verursacht werden.</string>
<string name="error_dialog_server_app_enabled">Bitte stellen Sie sicher, dass die „Notes“-App auf Ihrem Server installiert und aktiviert ist.</string>
<string name="error_dialog_redirect">Ihr Server antwortet mit HTTP Status-Code 302. Dies bedeutet, dass entweder die Notes App nicht auf Ihrem Server installiert ist, oder eine Fehlkonfiguration vorliegt. Die Ursache kann im Überschreiben von Variablen in der .htaccess-Datei liegen, oder an Nextcloud Apps wie OID Client.</string>
<string name="added_content">„%1$s“ hinzugefügt</string>
<string name="shared_text_empty">Der geteilte Text war ohne Inhalt</string>
<string name="append_to_note">An Notiz anhängen</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Δεν υπάρχει επαρκής χώρος αποθήκευσης στο Nextcloud. Διαγράψτε ορισμένα αρχεία για να συγχρονίσετε τις αλλαγές σας.</string>
<string name="error_dialog_contact_us">Μην διστάσετε να επικοινωνήσετε μαζί μας εάν τα προβλήματα παραμένουν. Μπορείτε να βρείτε τα στοιχεία επικοινωνίας μας στην ενότητα σχετικά στην πλευρική στήλη.</string>
<string name="error_dialog_we_need_info">Χρειαζόμαστε τις ακόλουθες τεχνικές πληροφορίες για να σας βοηθήσουμε:</string>
<string name="error_dialog_server_app_enabled">Παρακαλούμε βεβαιωθείτε ότι εγκαταστήσατε και ενεργοποιήσατε την εφαρμογή \"Notes\" στον διακομιστή σας.</string>
<string name="error_dialog_redirect">Ο διακομιστής απάντησε με κωδικό κατάστασης HTTP 302, που σημαίνει, πως δεν έχετε εγκατεστημένη την εφαρμογή Notes στον διακομιστή σας ή υπάρχει λάθος ρύθμιση. Αυτό μπορεί να προκλήθηκε από λάθος καταχώρηση στο αρχείο .htaccess-file ή σε εφαρμογές του Nextcloud όπως την OID Client.</string>
<string name="added_content">Προστέθηκε \"%1$s\"</string>
<string name="shared_text_empty">Το κοινόχρηστο κείμενο είναι κενό</string>
<string name="append_to_note">Προσάρτηση στη σημείωση</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -55,7 +55,7 @@
<string name="error_no_network">Sin conexión de red</string>
<string name="error_files_app">¿Ha instalado los archivos de la aplicación?</string>
<string name="error_token_mismatch">No se puede conectar a la aplicación archivos.</string>
<string name="error_insufficient_storage">lmacenamiento en el servidor está lleno.</string>
<string name="error_insufficient_storage">Tu espacio en el servidor está lleno.</string>
<string name="error_unknown">Ha ocurrido un error desconocido.</string>
<!-- About -->
@ -65,7 +65,7 @@
<string name="about_developers_title">Desarrolladores</string>
<string name="about_translators_title">Traductores</string>
<string name="about_translators_transifex">Comunidad de Nextcloud en &lt;a href=\"%1$s\">Transifex&lt;/a></string>
<string name="about_testers_title">Probadores</string>
<string name="about_testers_title">Testers</string>
<string name="about_source_title">Código fuente</string>
<string name="about_source">Este proyecto está alojado en GitHub: &lt;a href=\"%1$s\">%1$s&lt;/a></string>
<string name="about_issues_title">Problemas</string>
@ -113,9 +113,9 @@
<string name="category_movies">Películas</string>
<string name="category_movie">Película</string>
<string name="category_work">Trabajo</string>
<string name="account_already_imported">La cuenta ha ha sido importada</string>
<string name="account_already_imported">La cuenta ha sido importada</string>
<string name="no_notes_yet">Aún no hay notas</string>
<string name="no_notes_yet_description">rear una nota nueva</string>
<string name="no_notes_yet_description">Pulsa el botón + para crear una nota nueva</string>
<string name="could_not_load_preview_two_digit_numbered_list">No se ha podido cargar la vista previa. Por favor, comprueba si hay un ítem de lista numerada de dos dígitos sin contenido.</string>
<string name="simple_more">Más</string>
<string name="simple_move">Mover</string>
@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Su instancia de Nextcloud no tiene espacio libre de almacenamiento. Por favor elimine algunos archivos para sincronizar sus cambios locales con su nube.</string>
<string name="error_dialog_contact_us">Por favor, no dude en contactar con nosotros si el problema persiste. Puede encontrar nuestra información de contacto en la sección acerca de en la barra lateral.</string>
<string name="error_dialog_we_need_info">Necesitamos la siguiente información técnica para ayudarle:</string>
<string name="error_dialog_server_app_enabled">Por favor, asegúrate de que has instalado y activado la app «Notas» en tu servidor.</string>
<string name="error_dialog_redirect">Tu servidor respondió con el código de estado HTTP 302, lo que implica que no está instalada la aplicación Deck en su servidor o que algo está mal configurado. Esto puede estar causado por anulaciones personalizadas en un archivo .htaccess o por aplicaciones de Nexcloud como OID Client. </string>
<string name="added_content">Se ha añadido «%1$s»</string>
<string name="shared_text_empty">El texto compartido estaba vacío</string>
<string name="append_to_note">Añadir a la nota</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Zure Nextcloud instantziak ez du biltegiratze leku libre gehiagorik. Ezabatu fitxategi batzuk, aldaketa lokalak hodeiarekin sinkroniza daitezen.</string>
<string name="error_dialog_contact_us">Mesedez, arazoak jarraitzen baldin badu ez izan zalantzarik gurekin harremanetan jartzeko. Alboko barran aurki dezakezu gurekin kontaktatzeko informazioa.</string>
<string name="error_dialog_we_need_info">Zuri laguntzeko hurrengo informazio teknikoa behar dugu:</string>
<string name="error_dialog_server_app_enabled">Mesedez egiaztatu \"Notak\" aplikazioa instalatu eta gaitu duzula zure zerbitzarian.</string>
<string name="error_dialog_redirect">Zure zerbitzariak HTTP 302 egoera kodearekin erantzun du, honek esan nahi du ez duzula Notak aplikazioa ez duzula zure zerbitzarian instalatu edo zerbait txarto konfiguratuta dagoela. Hau .htaccess fitxategi batean arau pertsonalizatuak daudelako edo OID Bezeroa izeneko aplikazioarengatik sortua izan daiteke. </string>
<string name="added_content">\"%1$s\" gehituta</string>
<string name="shared_text_empty">Partekatutako testua hutsik zegoen</string>
<string name="append_to_note">Gehitu notara</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Votre instance Nextcloud n\'a plus d\'espace disponible. Veuillez supprimer certains fichiers pour pouvoir synchroniser vos modifications locales dans votre cloud.</string>
<string name="error_dialog_contact_us">N\'hésitez pas à nous contacter si les problèmes persistent. Vous trouverez nos coordonnées dans la section : À propos, de la barre latérale.</string>
<string name="error_dialog_we_need_info">Nous avons besoin des informations techniques suivantes pour pouvoir vous aider :</string>
<string name="error_dialog_server_app_enabled">Veuillez vérifier que vous avez installé et activé l\'application \"Notes\" sur votre serveur.</string>
<string name="error_dialog_redirect">Votre serveur a répondu avec le code HTTP 302, ce qui implique que vous n\'avez pas installé l\'application Notes sur votre serveur or que quelque chose est mal configuré. Ça peut être dû par des modifications du fichier .htaccess or par une autre application Nextcloud comme OID Client.</string>
<string name="added_content">\"%1$s\" ajouté</string>
<string name="shared_text_empty">Le texte partagé est vide</string>
<string name="append_to_note">Ajouter à la note</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">A súa instancia Nextcloud non dispón de espazo de almacenamento libre. Elimine algúns ficheiros para sincronizar os seus cambios locais na súa nube.</string>
<string name="error_dialog_contact_us">Non dubide en poñerse en contacto con nós se os problemas persisten. Pode atopar a nosa información de contacto na sección sobre na barra lateral.</string>
<string name="error_dialog_we_need_info">Necesitamos a seguinte información técnica para axudarlle:</string>
<string name="error_dialog_server_app_enabled">Asegúrese de ter instalada e activada a aplicación «Notas» no seu servidor.</string>
<string name="error_dialog_redirect">O seu servidor respondeu cun código de estado HTTP 302, o que implica que non ten instalada a aplicación Notas no servidor ou que algo está mal configurado. Isto pode ser causado por substitucións personalizadas nun ficheiro .htaccess ou por aplicacións Nextcloud como Client OID.</string>
<string name="added_content">Engadido «%1$s»</string>
<string name="shared_text_empty">O texto compartido estaba baleiro</string>
<string name="append_to_note">Anexo á nota</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -16,6 +16,8 @@
<string name="simple_bold">מודגש</string>
<string name="simple_link">קישור</string>
<string name="simple_italic">נטוי</string>
<string name="action_note_deleted">%1$s נמחק</string>
<string name="action_note_restored">%1$s שוחזר</string>
<string name="action_undo">ביטול</string>
<string name="action_drawer_open">פתיחת ניווט</string>
<string name="action_drawer_close">סגירת ניווט</string>
@ -130,6 +132,9 @@
<string name="could_not_copy_to_clipboard">לא ניתן להעתיק ללוח הגזירים</string>
<string name="error_dialog_title">שוד ושבר - מה עכשיו? 🙁</string>
<string name="error_dialog_we_need_info">אנו זקוקים לפירוט הטכני הבא כדי לסייע לך:</string>
<string name="shared_text_empty">הטקסט ששותף היה ריק</string>
<string name="append_to_note">הוספה לסוף הפתק</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">
<item>פתיחה במצב עריכה</item>

View file

@ -145,6 +145,7 @@
<string name="error_dialog_insufficient_storage">Nema slobodnog prostora za pohranu u vašoj instanci Nextclouda. Izbrišite dio datoteka kako biste sinkronizirali lokalne promjene u oblak.</string>
<string name="error_dialog_contact_us">Obratite nam se bez odlaganja ako problemi i dalje postoje. Naše podatke za kontakt možete pronaći u odjeljku s informacijama na bočnoj traci.</string>
<string name="error_dialog_we_need_info">Potrebne su nam sljedeće tehničke informacije kako bismo vam pomogli:</string>
<string name="error_dialog_redirect">Vaš je poslužitelj vratio šifru statusa HTTP 302, što znači da aplikacija Notes nije instalirana na poslužitelju ili postoji pogreška u konfiguraciji. To mogu uzrokovati prilagođena prepisivanja u datoteci .htaccess ili određene aplikacije u Nextcloudu, kao što je OID Client.</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">
<item>Otvori u načinu uređivanja</item>

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">La tua istanza di Nextcloud non ha più spazio libero. Elimina alcuni file per sincronizzare le modifiche locali con il tuo cloud.</string>
<string name="error_dialog_contact_us">Non esitare a contattarci se il problema persiste. Puoi trovare le nostre informazioni di contatto nella sezione Informazioni della barra laterale.</string>
<string name="error_dialog_we_need_info">Ci servono le seguenti informazioni tecniche per aiutarti:</string>
<string name="error_dialog_server_app_enabled">Assicurati di aver installato e abilitato l\'applicazione \"Note\" sul tuo server.</string>
<string name="error_dialog_redirect">Il tuo server ha risposto con un codice di stato HTTP 302, che implica che non hai installato l\'applicazione Note sul tuo server o qualcosa non è configurato correttamente. Questo può essere causato da configurazioni personalizzate nel file .htaccess o da applicazioni di Nextcloud come OID Client.</string>
<string name="added_content">Aggiunto \"%1$s\"</string>
<string name="shared_text_empty">Il testo condiviso era vuoto</string>
<string name="append_to_note">Aggiungi a nota</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -146,7 +146,6 @@
<string name="error_dialog_contact_us">問題が継続する場合はお問い合わせください。我々の連絡先はサイドバーのaboutセクションにかかれています。</string>
<string name="error_dialog_we_need_info">サポートには下記技術情報が必要です:</string>
<string name="error_dialog_redirect">サーバがHTTPステータスコード 302を返しました。これはNotesアプリがインストールされていないか、構成が誤っていることを意味します。.htaccessファイルのカスタムオーバーライドやOIDクライアントのようなNextcloudアプリが原因の可能性があります。</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">
<item>編集モードで開く</item>

View file

@ -146,7 +146,11 @@
<string name="error_dialog_insufficient_storage">Je Nextcloud instantie heeft geen vrije ruimte meer. Verwijder alstublieft enkele bestanden om je lokale gegevens te kunnen synchroniseren.</string>
<string name="error_dialog_contact_us">Schroom alstublieft niet om contact met ons op te nemen als het probleem blijft aanhouden. Onze contactgegevens zijn te vinden in de About sectie in de zijbalk.</string>
<string name="error_dialog_we_need_info">We hebben de volgende technische informatie nodig om je te kunnen helpen: </string>
<string name="error_dialog_server_app_enabled">Zorg er alstublieft voor dat je de \"Notes\" app hebt geïnstalleerd en aangezet op jouw server.</string>
<string name="error_dialog_redirect">Je server heeft gereageerd met een HTTP 302-statuscode, wat inhoudt dat je de Deck-app niet op je server hebt geïnstalleerd of dat er iets verkeerd is geconfigureerd. Dit kan worden veroorzaakt door maatwerk aanpassingen in een .htaccess-bestand of door Nextcloud-apps zoals de OID Client.</string>
<string name="added_content">\"%1$s\" toegevoegd</string>
<string name="shared_text_empty">Gedeelde tekst was leeg</string>
<string name="append_to_note">Achteraan toevoegen aan notitie</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">W Twojej instancji Nextcloud nie ma już wolnego miejsca. Usuń niektóre pliki, aby zsynchronizować lokalne zmiany w chmurze.</string>
<string name="error_dialog_contact_us">Jeśli problem będzie się powtarzał, skontaktuj się z nami. Nasze informacje kontaktowe można znaleźć w sekcji \"Informacje\" na pasku bocznym.</string>
<string name="error_dialog_we_need_info">Potrzebujemy następujących informacji technicznych, aby Ci pomóc:</string>
<string name="error_dialog_server_app_enabled">Upewnij się, że zainstalowałeś i włączyłeś aplikację \"Notes\" na swoim serwerze.</string>
<string name="error_dialog_redirect">Twój serwer odpowiedział kodem stanu HTTP 302, co oznacza, że nie masz zainstalowanej aplikacji Notes na serwerze lub coś jest źle skonfigurowane. Może to być spowodowane niestandardowymi przekierowaniami pliku .htaccess lub aplikacjami Nextcloud, takimi jak OID Client.</string>
<string name="added_content">Dodano \"%1$s\"</string>
<string name="shared_text_empty">Udostępniony tekst był pusty</string>
<string name="append_to_note">Dołącz do notatki</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Sua instância do Nextcloud não possui mais espaço livre. Exclua alguns arquivos para sincronizar suas alterações locais com a nuvem.</string>
<string name="error_dialog_contact_us">Não hesite em nos contactar se os problemas persistirem. Você pode encontrar informações de contato na seção \"sobre\" na barra lateral.</string>
<string name="error_dialog_we_need_info">Precisamos das seguintes informações técnicas para ajudá-lo:</string>
<string name="error_dialog_server_app_enabled">Verifique se você instalou e ativou o aplicativo \"Notes\" no seu servidor.</string>
<string name="error_dialog_redirect">Seu servidor respondeu com um código de status HTTP 302, o que implica que você não instalou o aplicativo Notes no servidor ou que algo está configurado incorretamente. Isso pode ser causado por alterações no arquivo .htaccess ou por aplicativos como o Client OID.</string>
<string name="added_content">\"%1$s\" adicionado</string>
<string name="shared_text_empty">O texto compartilhado estava vazio</string>
<string name="append_to_note">Anexar à nota</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">На вашем экземпляре Nextcloud нет свободного места. Удалите ненужные файлы для синхронизации изменений в облако.</string>
<string name="error_dialog_contact_us">Не стесняйтесь сообщать разработчикам о повторяющихся ошибках. Контактная информация приведена в боковой панели в разделе «О программе».</string>
<string name="error_dialog_we_need_info">Требуется следующая техническая информация от вас:</string>
<string name="error_dialog_server_app_enabled">Проверьте, установлено ли на сервере приложение Заметки.</string>
<string name="error_dialog_redirect">На запрос сервер вернул код состояния HTTP 302, что означает, что приложение Заметки либо не установлено, либо сервер настроен неверно. Такое поведение может являться следствием замены стандартного файла .htaccess администратором сервера или приложением Nextcloud, таким как OID Client.</string>
<string name="added_content">Добавлена заметка «%1$s»</string>
<string name="shared_text_empty">Публикуемый текст пустой</string>
<string name="append_to_note">Добавить в заметку</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Vo vašej inštancii Nexcloudu nie je na úložisku voľné miesto. Aby bolo možné synchronizovať vaše miestne zmeny s vaším cloudom, je potrebné najprv zmazať niektoré súbory.</string>
<string name="error_dialog_contact_us">Ak problémy pretrvávajú, neváhajte nás kontaktovať. Kontaktné informácie nájdete v časti \"O nás\" v bočnom paneli.</string>
<string name="error_dialog_we_need_info">Aby sme vám pomohli, potrebujeme následovné technické informácie:</string>
<string name="error_dialog_server_app_enabled">Uistite sa, že ste na serveri nainštalovali a povolili aplikáciu Poznámky.</string>
<string name="error_dialog_redirect">Váš server odpovedal stavovým kódom HTTP 302, čo znamená, že na svojom serveri nemáte nainštalovanú aplikáciu Notes alebo je niečo nesprávne nastavené. Môže to byť spôsobené vlastnými zmenami v súbore .htaccess alebo aplikáciami Nextcloud, ako je OID Client.</string>
<string name="added_content">Pridané \"%1$s\"</string>
<string name="shared_text_empty">Zdieľaný text bol prázdny</string>
<string name="append_to_note">Pripojiť k poznámke</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -129,6 +129,8 @@
<string name="unlock_notes">Odkleni zabeležke</string>
<string name="simple_beta">Preizkusno</string>
<string name="could_not_copy_to_clipboard">Predmeta ni mogoče kopirati v odložišče</string>
<string name="error_dialog_tip_token_mismatch_retry">Poskusite vsiliti končanje programa in ga ponovno zagnati. Morda je težava v neustrezni povezavi z okoljem Nextcloud</string>
<string name="error_dialog_tip_files_outdated">Program Nextcloud je očitno starejše različice. Posodobite jo prek Trgovine Play ali s posodabljalnikom F-Droid.</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">
<item>Zabeležka se odpre v urejevalnem načinu</item>

View file

@ -145,7 +145,11 @@
<string name="error_dialog_insufficient_storage">Nextcloud kopyanızda boş depolama alanı kalmamış. Lütfen yerel değişikliklerinizi bulut ile eşitlemek için bazı dosyaları silerek yer açın.</string>
<string name="error_dialog_contact_us">Sorun sürerse lütfen bize sormaktan çekinmeyin. İletişim bilgilerimizi yan çubuktaki hakkında bölümünde bulabilirsiniz.</string>
<string name="error_dialog_we_need_info">Size yardımcı olabilmemiz için şu teknik bilgilere gerek duyacağız:</string>
<string name="error_dialog_server_app_enabled">Lütfen sunucunuzda \"Notlar\" uygulamasının kurulu ve etkinleştirilmiş olduğundan emin olun.</string>
<string name="error_dialog_redirect">Sunucunuz, üzerine Notlar uygulamasının kurulmadığı ya da bir şeyin yanlış yapılandırıldığı anlamına gelen HTTP 302 durum kodu ile yanıt verdi. Bu durum, bir .htaccess dosyasındaki özel kısıtlamalardan ya da OID İstemcisi gibi Nextcloud uygulamalarından kaynaklanabilir.</string>
<string name="added_content">\"%1$s\" eklendi</string>
<string name="shared_text_empty">Paylaşılan metin boş</string>
<string name="append_to_note">Nota ekle</string>
<!-- Array: note modes -->
<string-array name="noteMode_entries">

View file

@ -0,0 +1 @@
- 🐞 Fix upgrading from version <= 1.0.1 to a current version

View file

@ -0,0 +1 @@
- Note list widget should create a note in it's respective category (#817)