Commit 81922d1d authored by Nishith  Khanna's avatar Nishith Khanna
Browse files

Merge changes from upstream

parents 4205c914 2761de7a
Pipeline #165470 failed with stage
in 11 seconds
\ No newline at end of file
\ No newline at end of file
[submodule "cert4android"]
path = cert4android
url = ../cert4android.git
branch = sprint
language: android
jdk: oraclejdk8
- tools
- build-tools-27.0.3
- android-28
- extra-android-m2repository
- yes | sdkmanager "platforms;android-27"
- yes | sdkmanager "platforms;android-28"
script: ./gradlew testDebug
......@@ -8,7 +8,7 @@ If you find a bug, feel free to [open an issue](
**Device**: e. g. Motorola Moto G 2015
**System language**: English (US), German, ...
**System language**: English (US), German,
**App version:** e. g. v0.8.1
......@@ -18,14 +18,14 @@ If you find a bug, feel free to [open an issue](
1. open the app
2. click on a note
3. use the top left back-arrow
4. ...
### Copy & Paste
**Android version:** e. g. 6.0.1 Marshmallow
**Device**: e. g. Motorola Moto G 2015
**System language**: English (US), German, ...
**System language**: English (US), German,
**App version:** e. g. v0.8.1
......@@ -35,7 +35,7 @@ If you find a bug, feel free to [open an issue](
1. open the app
2. click on a note
3. use the top left back-arrow
4. ...
## Adding new features
# Frequently asked questions
- [Why aren't there any buttons to apply formatting?](
- [I have experienced an error](
- [`NextcloudApiNotRespondingException`](
- [`UnknownErrorException: Read timed out`](
- [`IllegalStateException: Duplicate key`](
- [`NextcloudFilesAppAccountNotFoundException`](
- [`TokenMismatchException`](
- [Workarounds](
- [Why don't you make an option for…?](
- [Why is there no support for pens?](
- [Why has my bug report been closed?](
- [How can i activate the dark mode for widgets?](
## Why aren't there any buttons to apply formatting
We use context based formatting to avoid distractions while writing. This is not "Word on Android".
You have some shortcuts available in a context, e.g.
- when you select some text, you can make it bold, italic, insert a link, etc.:
![Selection formatting](
- when you hit the selector thumb without selected text, you will have actions in the context of the current line, like making it a checkbox:
![Line formatting](
This approach allows us to only show the actions that make sense for the current context.
We plan to extend this system further in the future and might add toggles for headlines etc.
## I have experienced an error
Sorry. There are so many different environments, that it is impossible for us to test each and every constellation.
First of all make sure you have updated to and tried with the latest available versions of this app, the [Nextcloud Android]( app and the [Notes server app](
### `NextcloudApiNotRespondingException`
Try to disable the battery "optimization" for Notes Android and Nextcloud Android. Some manufacturers prevent the Notes Android app from communicating with the Nextcloud Android app properly. It is recommended to clear the storage of both apps as [explained below](#workarounds).
This is a [known issue of the SingleSignOn mechanism]( which we only can work around but not solve on our side.
### `UnknownErrorException: Read timed out`
This issue is caused by a connection time out. This can be the case if there are infrastructural or environmental problems (like a misconfigured server or a bad network connection).
Probably you will experience it when importing an account, because at this moment, all your Notes will getting downloaded. Given you have a lots of notes, this might take longer than the connection is available.
Further synchronizations are usually not causing this issue, because the Notes app tries to synchronize only *changed* notes after the first import.
If your notes are not ten thousands of characters long, it is very unlikely that this causes a connection timeout.
We improved the import of an account in version `3.4.12` to make it more reliable by [fetching notes step by step](
If you are using an older version, you can as a workaround for the first import try to
1. move all your notes to a different folder on your Nextcloud instance
2. import your account on your smartphone
3. put your notes back to the original folder step by step and sync everytime you put some notes back
### `IllegalStateException: Duplicate key`
This is issue was caused by a bug which was present in the Notes Android app between `3.4.0` and `3.4.10`. It has been fixed in `3.4.11`, though it created a corrupt database state which is not recoverable automatically without data loss. It is therefore required to [clear the storage of the Notes Android app]( and import your account again from scratch. Make sure to backup unsynchronized changes before doing this.
### `NextcloudFilesAppAccountNotFoundException`
We are not yet sure what exactly causes this issue, but investigate it by [adding more debug logs to recent versions]( In theory this might happen if an already imported account has been deleted in the Nextcloud app.
As a workaround you can remove the account (or clear the storage of the app as described below if you can't access the account manager anymore) and import it again.
### `TokenMismatchException`
The reason of this error is not yet clear. It often seems to be connected to changes of the authentication (for example enabling 2FA after some time). Please clear the storage of both, the Notes and the Nextcloud Android apps as described in the [workarounds]( section.
### Workarounds
In some cases, clearing the storage of the Notes Android app and restarting with a clean state will already be enough. Since we use the [Single Sign On mechanism]( of Nextcloud, it might be necessary to clear the storage of **both** apps, Notes Android **and** Nextcloud Android. (the Nextcloud Android app manages some parts of the authentication and the network stack).
- ⚠️ Not yet synchronized changes will be lost by performing this step.
- ⚠️ Uninstalling an app is **not** the same as clearing the storage, since Android keeps some data left on your device when uninstalling an app.
You can achieve this by navigating to
Android settings
Nextcloud / Notes
Clear storage
Then set up your account in the Nextcloud Android app again and import the configured account in the Notes Android app.
If the issue persists, [open a bug report in our issue tracker](
## Why don't you make an option for…?
We prefer good defaults over providing an option for each edge case. Our resources are quite limited, so we have to consider introducing new options very carefully.
1. A feature is implemented quickly, but who will maintain it for the next 5 years?
2. Each option increases the test matrix exponentially and leads to huge efforts to test every combination
3. Each option increases the possible constellations, making it hard to track down issues
4. Each option increases the visual noise for people who will *not* use the options
5. Each option increases the maintenance efforts, making it harder over the time to work on actual features
6. Each option introduces new side effects, which might lead to undiscovered bugs or break existing features
7. The Android app aims to mirror feature parity with the corresponding server app
## Why is there no support for pens?
This topic has been requested multiple times and we'd love to support pens. There are some obstacles, though:
### Choice of approach
Handwritten notes can be implemented in various ways - for example as attachments or just recognizing the characters and translate them to text. The first approach depends on attachments support for the Notes server app (currently [work in progress](
### Licensing issues
The Notes Android app was, is and will always be free. [Free not as in "free beer" but as in "freedom"]( I therefore will not accept any solution that requires to include proprietary libraries (also not for the Google Play Store flavor).
Since i am not aware of any proper free SDK for pens, I recommend you to ask your manufacturer to publish a development SDK under a free license and ask them why they sell stuff to you which you in fact do not own.
### Hardware issues
Given a [free SDK](#licensing-issues) can be found, there is another issue: I don't own a device with a pen. I welcome [Pull Requests]( with contributions to this topic, but i can and will not buy a new device just for this aspect, sorry.
## Why has my bug report been closed?
As stated in the bug templates, we reserve to close issues which do not fill the **complete issue template**. The information we ask for is urgently needed, even if it might not seem to be important or relevant to you.
We have very limited resources and capacity and we really want to help you fixing different bugs, but we can impossibly know your environment, your different software versions, the store you used.
Therefore it is extremely important for you to describe the **exact steps to reproduce**. This includes information about your environment.
Example for a bad description:
> 1. The app crashes when i save a note
Example for a good description:
> 1. Open any existing note
> 2. Change category to another existing category
> 3. Click on the ⇦ in the top left
> 4. See app crash
We also preserve to close issues where the **original reporter does not answer within a certain time frame**. We usually answer issues within a hour and expect you to respond to our questions within a week.
This is necessary for two reasons:
1. We have a rapid development cycle - bugs which have been reported weeks ago might no longer relevant
2. We are loosing the context of a report or a question over the time. We have many things to care about and digging into an issue deep and then relying on an response which is not coming is a waste of our limited free time
## How can i activate the dark mode for widgets?
Since `v3.2.0` the widgets are using the **global Android setting**. You can change it in the Android settings, depending on your manufacturer probably under the "Display" menu item:
![Enable global Android dark mode](
The main reason is a better and tighter integration in the default android theming mechanism. For example Android will switch the complete system UI to a dark mode when the battery is low.
The widgets have previously not respected those Android intentions, also they ignored the user setting mentioned above for a overall-same theme on the device.
The dark mode of the app does *not* affect the appearance of the widgets because the context is different.
While the app is something one starts intentionally and runs in its own context, the widgets run always in the context of the launcher.
To provide a homogeneous interface in your launcher and take full benefits of OLED screens and battery saving mechanisms, Google implemented the global Android setting in Android 10 to affect everything in this context at once - the app drawer, the status bar and the widgets.
According to the Play Store statistics when we release `v3.2.0`, more than `73%` of our (Play Store) users used Android 10 or higher and therefore the global setting is already present for them.
Further `24%` used Android 7 - Android 9 and can utilize [tweaks]( (or other workarounds if they prefer to stay on old and partially even by Google [abandoned Android versions without security fixes]( instead of using a modern Custom ROM or other alternatives).
The efforts and benefits of a) maintaining a custom hacky dark mode vs. b) supporting natively the global dark mode inclusive auto-theming on low battery etc. is in absolutely no proportion anymore.
The same applies to the widgets of the Nextcloud Deck App and the Nextcloud News App.
# Nextcloud Notes Android Privacy Policy
The "Nextcloud Notes Android" Android-App (in the following referred to as "App") does not collect or send any data from you or your device to a server of the developers or the [Nextcloud GmbH]( The App sends all data exclusively to the server configured by you with the intention to synchronize the contents of the App with those of the server. This data can contain IP-addresses, timestamps and further information as meta data.
It is important to mention that all contents of the App may also be transmitted to the configured server. This contents can also contain personal information depending on the use. The servers you configured are technically outside the access area of this App developers, so that we neither know nor can prevent what happens to your data there. Please consult the privacy policy of the respective server operator.
The license of this project allows you to verify that no data is collected by the creators by reading the source code or asking someone else to do it.
## Permissions
This is a list of permissions required and asked by the App in order to properly work on your device:
- `android.permission.INTERNET`
Used by [Nextcloud Single Sign On library]( to communicate with your Nextcloud instance and synchronize contents.
- `android.permission.ACCESS_NETWORK_STATE`
Used to provide offline support and make the "Sync only on Wi-Fi" option possible.
- `android.permission.GET_ACCOUNTS`
Used by [Nextcloud Single Sign On library]( to read available accounts to import.
- `android.permission.WAKE_LOCK`
Used by [AndroidX WorkManager]( for background synchronization.
- `android.permission.RECEIVE_BOOT_COMPLETED`
Used by [AndroidX WorkManager]( for background synchronization.
- `android.permission.FOREGROUND_SERVICE`
Used by [AndroidX WorkManager]( for background synchronization.
## Nextcloud privacy policy
You can get more information on Nextcloud general privacy policy which is accessible at [](
\ No newline at end of file
# SSO Announcment
The next version of 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]( 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.
<?xml version="1.0" encoding="UTF-8"?>
<classpathentry kind="con" path=""/>
<classpathentry exported="true" kind="con" path=""/>
<classpathentry exported="true" kind="con" path=""/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
<?xml version="1.0" encoding="UTF-8"?>
apply plugin: ''
android {
compileSdkVersion 28
buildToolsVersion '29.0.1'
compileSdkVersion 31
buildToolsVersion '31.0.0'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
defaultConfig {
applicationId "foundation.e.notes"
minSdkVersion 24
targetSdkVersion 28
versionCode 49
versionName "1.0.1"
minSdkVersion 23
targetSdkVersion 31
versionCode 3004018
versionName "3.4.18"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
buildFeatures {
viewBinding true
buildTypes {
debug {
applicationIdSuffix ".debug"
testCoverageEnabled true
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), ''
lintOptions {
disable 'MissingTranslation'
abortOnError false
dataBinding {
enabled = true
testOptions {
unitTests {
includeAndroidResources true
aaptOptions {
additionalParameters '-I', 'e-ui-sdk.jar'
lint {
abortOnError false
disable 'MissingTranslation'
dependencies {
compileOnly files("../e-ui-sdk.jar")
coreLibraryDesugaring ''
// Nextcloud SSO
implementation 'com.github.nextcloud:Android-SingleSignOn:0.6.1'
implementation 'com.github.stefan-niedermann:android-commons:0.2.5'
implementation 'com.github.stefan-niedermann.nextcloud-commons:sso-glide:1.6.2'
implementation 'com.github.stefan-niedermann.nextcloud-commons:exception:1.6.2'
implementation('com.github.stefan-niedermann.nextcloud-commons:markdown:1.6.2') {
exclude group: 'org.jetbrains', module: 'annotations-java5'
// Glide
implementation 'com.github.bumptech.glide:glide:4.13.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.0'
implementation project(':cert4android')
// Android X
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.fragment:fragment:1.4.1'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation ''
implementation ''
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'io.reactivex:rxjava:1.3.8'
implementation 'com.yydcdut:markdown-processor:0.1.3'
implementation 'com.yydcdut:rxmarkdown-wrapper:0.1.3'
// Database
implementation ''
annotationProcessor ''
implementation ''
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
// Gson
implementation ''
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
// ReactiveX
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation ""
// Testing
testImplementation 'androidx.test:core:1.4.0'
testImplementation 'androidx.arch.core:core-testing:2.1.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:4.3.1'
testImplementation 'org.robolectric:robolectric:4.7.3'
implementation fileTree(dir: 'libs', include: ['*.jar'])
# To enable ProGuard in your project, edit
# to define the proguard.config property as described in that file.
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in
# For more details, see
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# This file must be checked in Version Control Systems.
# To customize properties used by the Ant build system edit
# "", and override values to adapt the script to your
# project structure.
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
# Project target.
package foundation.e.notes;
import android.test.ApplicationTestCase;
* <a href="">Testing Fundamentals</a>
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
\ No newline at end of file
package foundation.e.notes.model;
import junit.framework.TestCase;
import java.util.Calendar;
* Tests the Note Model
* Created by stefan on 06.10.15.
public class NoteTest extends TestCase {
public void testMarkDownStrip() {
CloudNote note = new CloudNote(0, Calendar.getInstance(), "#Title", "", false, null, null);
note.setTitle("* Aufzählung");
package foundation.e.notes.util;
import junit.framework.TestCase;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
* Tests the NoteUtil
* Created by stefan on 06.10.15.
public class NoteUtilTest extends TestCase {
public void testRemoveMarkDown() {
assertEquals("Test", NoteUtil.removeMarkDown("Test"));
assertEquals("Foo\nBar", NoteUtil.removeMarkDown("Foo\nBar"));
assertEquals("Foo\nBar", NoteUtil.removeMarkDown("Foo\n Bar"));
assertEquals("Foo\nBar", NoteUtil.removeMarkDown("Foo \nBar"));
assertEquals("Foo-Bar", NoteUtil.removeMarkDown("Foo-Bar"));
assertEquals("Foo*Bar", NoteUtil.removeMarkDown("Foo*Bar"));
assertEquals("Foo/Bar", NoteUtil.removeMarkDown("Foo/Bar"));
assertEquals("FooTestBar", NoteUtil.removeMarkDown("Foo*Test*Bar"));
assertEquals("FooTestBar", NoteUtil.removeMarkDown("Foo**Test**Bar"));
assertEquals("FooTestBar", NoteUtil.removeMarkDown("Foo***Test***Bar"));
assertEquals("FooTest*Bar", NoteUtil.removeMarkDown("Foo*Test**Bar"));
assertEquals("Foo*TestBar", NoteUtil.removeMarkDown("Foo***Test**Bar"));
assertEquals("FooTestBar", NoteUtil.removeMarkDown("Foo_Test_Bar"));
assertEquals("FooTestBar", NoteUtil.removeMarkDown("Foo__Test__Bar"));
assertEquals("FooTestBar", NoteUtil.removeMarkDown("Foo___Test___Bar"));
assertEquals("Foo\nHeader\nBar", NoteUtil.removeMarkDown("Foo\n# Header\nBar"));
assertEquals("Foo\nHeader\nBar", NoteUtil.removeMarkDown("Foo\n### Header\nBar"));
assertEquals("Foo\nHeader\nBar", NoteUtil.removeMarkDown("Foo\n# Header #\nBar"));
assertEquals("Foo\nHeader\nBar", NoteUtil.removeMarkDown("Foo\n## Header ####\nBar"));
assertEquals("Foo\nNo Header #\nBar", NoteUtil.removeMarkDown("Foo\nNo Header #\nBar"));
assertEquals("Foo\nHeader\nBar", NoteUtil.removeMarkDown("Foo\nHeader\n=\nBar"));
assertEquals("Foo\nHeader\nBar", NoteUtil.removeMarkDown("Foo\nHeader\n-----\nBar"));
assertEquals("Foo\nHeader\n--=--\nBar", NoteUtil.removeMarkDown("Foo\nHeader\n--=--\nBar"));
assertEquals("Foo\nAufzählung\nBar", NoteUtil.removeMarkDown("Foo\n* Aufzählung\nBar"));
assertEquals("Foo\nAufzählung\nBar", NoteUtil.removeMarkDown("Foo\n+ Aufzählung\nBar"));
assertEquals("Foo\nAufzählung\nBar", NoteUtil.removeMarkDown("Foo\n- Aufzählung\nBar"));
assertEquals("Foo\nAufzählung\nBar", NoteUtil.removeMarkDown("Foo\n - Aufzählung\nBar"));
assertEquals("Foo\nAufzählung *\nBar", NoteUtil.removeMarkDown("Foo\n* Aufzählung *\nBar"));
public void testIsEmptyLine() {
try {
Method m = NoteUtil.class.getDeclaredMethod("isEmptyLine");
assertTrue((Boolean) m.invoke(null, " "));
assertTrue((Boolean) m.invoke(null, "\n"));
assertTrue((Boolean) m.invoke(null, "\n "));
assertTrue((Boolean) m.invoke(null, " \n"));
assertTrue((Boolean) m.invoke(null, " \n "));
assertFalse((Boolean) m.invoke(null, "a \n "));
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
public void testGetLineWithoutMarkDown() {