diff --git a/.gitignore b/.gitignore
index 5317e8f4d4bd352a215c4f643f5c6c9f85b1798c..f6ab0018df367c1e38774ab4c60130aa84eeaa16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
/.idea/
*.iml
/projectFilesBackup/
+/app/release/
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 1540e1c061cdd6a8f84562b68cc734053c6f6228..160a83c5c448c4190e7d48a1032cce74f632c708 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "cert4android"]
path = cert4android
url = ../cert4android.git
+ branch = sprint
diff --git a/.travis.yml b/.travis.yml
index a4c91584abd3849c42b4130a5d74ddae26fd6b8a..5321fcb30dc965dc44c3fce04f88af9d4c75ba4f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,10 +6,11 @@ android:
components:
- tools
- build-tools-27.0.3
- - android-27
+ - android-28
- extra-android-m2repository
before_install:
- yes | sdkmanager "platforms;android-27"
+ - yes | sdkmanager "platforms;android-28"
script: ./gradlew testDebug
diff --git a/README.md b/README.md
index 0c829084f6eafe76c22ae2339ba1fd23a1316940..e29dfe634b4c249636046aadb799cf948909e752 100644
--- a/README.md
+++ b/README.md
@@ -8,28 +8,41 @@ An android client for [Nextcloud Notes App](https://github.com/nextcloud/notes/)
## :arrow_forward: Access
+<<<<<<< HEAD
[](https://play.google.com/store/apps/details?id=it.niedermann.notes)
[](https://f-droid.org/repository/browse/?fdid=it.niedermann.notes)
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K7HVLE6J7SXXA)
+=======
+[
](https://play.google.com/store/apps/details?id=it.niedermann.owncloud.notes)
+[
](https://f-droid.org/repository/browse/?fdid=it.niedermann.owncloud.notes)
+[
](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K7HVLE6J7SXXA)
+>>>>>>> steff_master
[](https://liberapay.com/stefan-niedermann/donate)
## :eyes: Screenshots
-
-
+| Navi | List View | Edit Mode |
+| :--: | :--: | :--: |
+|  |  |  |
## :rocket: Features
* List, create, edit, share, search and delete notes
* Share text and links as new note into the app
* Mark notes as favorite
* Bulk delete
+* In-note search ([#106](https://github.com/stefan-niedermann/nextcloud-notes/issues/106))
* Render MarkDown (using [RxMarkdown](https://github.com/yydcdut/RxMarkdown))
* Translated in many languages on [Transifex](https://www.transifex.com/nextcloud/nextcloud/android-notes/)
+* Context based formatting ([#363](https://github.com/stefan-niedermann/nextcloud-notes/issues/363))
## :checkered_flag: Planned features
-* In-note search ([#106](https://github.com/stefan-niedermann/nextcloud-notes/issues/106))
* Toggle checkboxes in view mode ([#451](https://github.com/stefan-niedermann/nextcloud-notes/issues/451))
-* Context based formatting ([#363](https://github.com/stefan-niedermann/nextcloud-notes/issues/363))
* Trashbin ([#238](https://github.com/stefan-niedermann/nextcloud-notes/issues/238))
## :family: Join the team
diff --git a/SSO Announcment.md b/SSO Announcment.md
new file mode 100644
index 0000000000000000000000000000000000000000..27553224981fc5b4e786a6812d38681f26f54920
--- /dev/null
+++ b/SSO Announcment.md
@@ -0,0 +1,20 @@
+# 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](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.
diff --git a/app/build.gradle b/app/build.gradle
index cdde7a8edf39d70d64e50311d50d9c6e0d280257..6be513ba46d0be7c85009fd1a253aca1872460fd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,14 +2,19 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 28
- buildToolsVersion '28.0.3'
+ buildToolsVersion '29.0.1'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
defaultConfig {
applicationId "foundation.e.notes"
minSdkVersion 24
targetSdkVersion 28
- versionCode 37
- versionName "0.22.3"
+ versionCode 49
+ versionName "1.0.1"
}
buildTypes {
release {
@@ -21,26 +26,30 @@ android {
disable 'MissingTranslation'
abortOnError false
}
+ dataBinding {
+ enabled = true
+ }
}
dependencies {
implementation project(':cert4android')
- implementation 'io.reactivex:rxandroid:1.2.0'
- implementation 'io.reactivex:rxjava:1.1.5'
+ 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'
- implementation 'com.jakewharton:butterknife:8.8.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
- def supportLibVersion = '28.0.0'
+ implementation 'com.github.bumptech.glide:glide:4.10.0'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
+
+ implementation 'com.jakewharton:butterknife:10.2.0'
+ annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
+
+ implementation "androidx.appcompat:appcompat:1.1.0"
+ implementation "androidx.recyclerview:recyclerview:1.0.0"
+ implementation "com.google.android.material:material:1.0.0"
- implementation "com.android.support:appcompat-v7:${supportLibVersion}"
- implementation "com.android.support:design:${supportLibVersion}"
- implementation "com.android.support:recyclerview-v7:${supportLibVersion}"
- // needed for cert4android (conflict resolution)
- implementation "com.android.support:cardview-v7:${supportLibVersion}"
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index aa13052eecd6672c8ae1e8c0be428ff23c1a5ad0..e8dee799b53febc638fb247ca9796b0786518e50 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -70,6 +70,12 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/foundation/e/notes/android/AlwaysAutoCompleteTextView.java b/app/src/main/java/foundation/e/notes/android/AlwaysAutoCompleteTextView.java
index c439896b5e337647635e5689503e6ad5dd0f87c0..cdc62c5ca2db5d3a11080286cdbdc23210febce2 100644
--- a/app/src/main/java/foundation/e/notes/android/AlwaysAutoCompleteTextView.java
+++ b/app/src/main/java/foundation/e/notes/android/AlwaysAutoCompleteTextView.java
@@ -1,7 +1,7 @@
package foundation.e.notes.android;
import android.content.Context;
-import android.support.v7.widget.AppCompatAutoCompleteTextView;
+import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.WindowManager;
diff --git a/app/src/main/java/foundation/e/notes/android/activity/AboutActivity.java b/app/src/main/java/foundation/e/notes/android/activity/AboutActivity.java
index d0ccad11225aa055efc2a34a8d3e67e269718138..6136a00d315d12e2bfc093827abea6d8a8ec825b 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/AboutActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/AboutActivity.java
@@ -1,12 +1,14 @@
package foundation.e.notes.android.activity;
import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v7.app.AppCompatActivity;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -14,9 +16,7 @@ import foundation.e.notes.android.fragment.about.AboutFragmentContributingTab;
import foundation.e.notes.android.fragment.about.AboutFragmentCreditsTab;
import foundation.e.notes.android.fragment.about.AboutFragmentLicenseTab;
import foundation.e.notes.R;
-import foundation.e.notes.android.fragment.about.AboutFragmentContributingTab;
-import foundation.e.notes.android.fragment.about.AboutFragmentCreditsTab;
-import foundation.e.notes.android.fragment.about.AboutFragmentLicenseTab;
+import foundation.e.notes.util.ExceptionHandler;
public class AboutActivity extends AppCompatActivity {
@@ -28,6 +28,7 @@ public class AboutActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
setContentView(R.layout.activity_about);
ButterKnife.bind(this);
@@ -87,4 +88,10 @@ public class AboutActivity extends AppCompatActivity {
}
}
}
-}
\ No newline at end of file
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ finish(); // close this activity as oppose to navigating up
+ return true;
+ }
+}
diff --git a/app/src/main/java/foundation/e/notes/android/activity/AccountActivity.java b/app/src/main/java/foundation/e/notes/android/activity/AccountActivity.java
index b763ec5dc87846b7d44e509deaec60c9ff172af2..66f8dcd30547f4983c2993c70d7a69eb22f26ab6 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/AccountActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/AccountActivity.java
@@ -5,7 +5,7 @@ import android.accounts.AccountManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.support.v7.app.AppCompatActivity;
+import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
diff --git a/app/src/main/java/foundation/e/notes/android/activity/EditNoteActivity.java b/app/src/main/java/foundation/e/notes/android/activity/EditNoteActivity.java
index b6031c120cbf1048699600ddf0258c65b283bb05..5feaabf4aef950cb3c82a5fe5dc94dcfa18b7a25 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/EditNoteActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/EditNoteActivity.java
@@ -5,28 +5,30 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+
import java.util.Calendar;
import foundation.e.notes.android.fragment.BaseNoteFragment;
import foundation.e.notes.android.fragment.NoteEditFragment;
import foundation.e.notes.android.fragment.NotePreviewFragment;
import foundation.e.notes.R;
-import foundation.e.notes.android.fragment.BaseNoteFragment;
-import foundation.e.notes.android.fragment.NoteEditFragment;
-import foundation.e.notes.android.fragment.NotePreviewFragment;
import foundation.e.notes.model.Category;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.model.DBNote;
import foundation.e.notes.util.NoteUtil;
+import foundation.e.notes.util.ExceptionHandler;
public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragment.NoteFragmentListener {
+ public static final String ACTION_SHORTCUT = "it.niedermann.owncloud.notes.shortcut";
+ private static final String INTENT_GOOGLE_ASSISTANT = "com.google.android.gm.action.AUTO_SEND";
+ private static final String MIMETYPE_TEXT_PLAIN = "text/plain";
public static final String PARAM_NOTE_ID = "noteId";
public static final String PARAM_CATEGORY = "category";
@@ -35,6 +37,7 @@ public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragm
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
if (savedInstanceState == null) {
launchNoteFragment();
@@ -118,6 +121,7 @@ public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragm
} else {
fragment = NotePreviewFragment.newInstance(noteId);
}
+
if (savedState != null) {
fragment.setInitialSavedState(savedState);
}
@@ -140,7 +144,11 @@ public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragm
}
String content = "";
- if (Intent.ACTION_SEND.equals(intent.getAction()) && "text/plain".equals(intent.getType())) {
+ if (
+ MIMETYPE_TEXT_PLAIN.equals(intent.getType()) &&
+ (Intent.ACTION_SEND.equals(intent.getAction()) ||
+ INTENT_GOOGLE_ASSISTANT.equals(intent.getAction()))
+ ) {
content = intent.getStringExtra(Intent.EXTRA_TEXT);
}
@@ -201,9 +209,9 @@ public class EditNoteActivity extends AppCompatActivity implements BaseNoteFragm
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(note.getTitle());
- if(!note.getCategory().isEmpty()) {
+ if (!note.getCategory().isEmpty()) {
actionBar.setSubtitle(NoteUtil.extendCategory(note.getCategory()));
}
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/foundation/e/notes/android/activity/ExceptionActivity.java b/app/src/main/java/foundation/e/notes/android/activity/ExceptionActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..7a961e5bc0549c67c4754f28d45e2efc6949d8bb
--- /dev/null
+++ b/app/src/main/java/foundation/e/notes/android/activity/ExceptionActivity.java
@@ -0,0 +1,64 @@
+package foundation.e.notes.android.activity;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Objects;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import foundation.e.notes.BuildConfig;
+import foundation.e.notes.R;
+
+public class ExceptionActivity extends AppCompatActivity {
+
+ Throwable throwable;
+
+ @BindView(R.id.message)
+ TextView message;
+ @BindView(R.id.stacktrace)
+ TextView stacktrace;
+
+ public static final String KEY_THROWABLE = "T";
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ setContentView(R.layout.activity_exception);
+ ButterKnife.bind(this);
+ super.onCreate(savedInstanceState);
+ throwable = ((Throwable) getIntent().getSerializableExtra(KEY_THROWABLE));
+ throwable.printStackTrace();
+ Objects.requireNonNull(getSupportActionBar()).setTitle(getString(R.string.simple_error));
+ this.message.setText(throwable.getMessage());
+ this.stacktrace.setText("Version: " + BuildConfig.VERSION_NAME + "\n\n" + getStacktraceOf(throwable));
+ }
+
+ private String getStacktraceOf(Throwable e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ return sw.toString();
+ }
+
+
+ @OnClick(R.id.copy)
+ void copyStacktraceToClipboard() {
+ final ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ ClipData clipData = ClipData.newPlainText(getString(R.string.simple_exception), this.stacktrace.getText());
+ clipboardManager.setPrimaryClip(clipData);
+ Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
+ }
+
+ @OnClick(R.id.close)
+ void close() {
+ finish();
+ }
+}
diff --git a/app/src/main/java/foundation/e/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/foundation/e/notes/android/activity/NotesListViewActivity.java
index ed418683f0e948d398b984c27bc6239a645aa6d4..9df7679998a8799497300f6cd85174f21d6f23b1 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/NotesListViewActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/NotesListViewActivity.java
@@ -5,38 +5,48 @@ import android.accounts.AccountManager;
import android.app.SearchManager;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
import android.content.res.Configuration;
import android.graphics.Canvas;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.provider.Settings;
-import android.support.annotation.Nullable;
-import android.support.design.widget.CoordinatorLayout;
-import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.Snackbar;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.view.ActionMode;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SearchView;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.appcompat.view.ActionMode;
+import androidx.appcompat.widget.AppCompatImageView;
+import androidx.appcompat.widget.SearchView;
+import androidx.appcompat.widget.Toolbar;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.android.material.snackbar.Snackbar;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -52,6 +62,7 @@ import foundation.e.notes.model.NavigationAdapter;
import foundation.e.notes.persistence.LoadNotesListTask;
import foundation.e.notes.persistence.NoteSQLiteOpenHelper;
import foundation.e.notes.persistence.NoteServerSyncHelper;
+import foundation.e.notes.util.ExceptionHandler;
import foundation.e.notes.util.ICallback;
import foundation.e.notes.util.NoteUtil;
import foundation.e.notes.util.NotesClientUtil;
@@ -65,12 +76,17 @@ import static foundation.e.notes.android.activity.SettingsActivity.SETTINGS_USER
* @author Nihar Thakkar
*/
+import static foundation.e.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT;
+
public class NotesListViewActivity extends AppCompatActivity implements ItemAdapter.NoteClickListener {
public final static String CREATED_NOTE = "foundation.e.notes.created_notes";
public final static String CREDENTIALS_CHANGED = "foundation.e.notes.CREDENTIALS_CHANGED";
public static final String ADAPTER_KEY_RECENT = "recent";
public static final String ADAPTER_KEY_STARRED = "starred";
+ public static final String ACTION_FAVORITES = "foundation.e.notes.favorites";
+ public static final String ACTION_RECENT = "foundation.e.notes.recent";
+
private static final String SAVED_STATE_NAVIGATION_SELECTION = "navigationSelection";
private static final String SAVED_STATE_NAVIGATION_ADAPTER_SLECTION = "navigationAdapterSelection";
@@ -91,6 +107,10 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
Toolbar toolbar;
@BindView(R.id.drawerLayout)
DrawerLayout drawerLayout;
+ @BindView(R.id.current_account_image)
+ AppCompatImageView currentAccountImage;
+ @BindView(R.id.header_view)
+ RelativeLayout headerView;
@BindView(R.id.account)
TextView account;
@BindView(R.id.swiperefreshlayout)
@@ -124,6 +144,29 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
}
refreshLists();
swipeRefreshLayout.setRefreshing(false);
+ new Thread(() -> {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
+ ShortcutManager shortcutManager = getApplicationContext().getSystemService(ShortcutManager.class);
+ if (!shortcutManager.isRateLimitingActive()) {
+ List newShortcuts = new ArrayList<>();
+
+ for (DBNote note : db.getRecentNotes()) {
+ Intent intent = new Intent(getApplicationContext(), EditNoteActivity.class);
+ intent.putExtra(EditNoteActivity.PARAM_NOTE_ID, note.getId());
+ intent.setAction(ACTION_SHORTCUT);
+
+ newShortcuts.add(new ShortcutInfo.Builder(getApplicationContext(), note.getId() + "")
+ .setShortLabel(note.getTitle())
+ .setIcon(Icon.createWithResource(getApplicationContext(), note.isFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp))
+ .setIntent(intent)
+ .build());
+ }
+ Log.d(getClass().getSimpleName(), "Update dynamic shortcuts");
+ shortcutManager.removeAllDynamicShortcuts();
+ shortcutManager.addDynamicShortcuts(newShortcuts);
+ }
+ }
+ }).run();
}
@Override
@@ -134,6 +177,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
// First Run Wizard
if (!NoteServerSyncHelper.isConfigured(this)) {
Intent settingsIntent = new Intent(this, AccountActivity.class);
@@ -150,7 +194,14 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
}
String categoryAdapterSelectedItem = ADAPTER_KEY_RECENT;
- if (savedInstanceState != null) {
+ if (savedInstanceState == null) {
+ if (ACTION_RECENT.equals(getIntent().getAction())) {
+ categoryAdapterSelectedItem = ADAPTER_KEY_RECENT;
+ } else if (ACTION_FAVORITES.equals(getIntent().getAction())) {
+ categoryAdapterSelectedItem = ADAPTER_KEY_STARRED;
+ navigationSelection = new Category(null, true);
+ }
+ } else {
navigationSelection = (Category) savedInstanceState.getSerializable(SAVED_STATE_NAVIGATION_SELECTION);
navigationOpen = savedInstanceState.getString(SAVED_STATE_NAVIGATION_OPEN);
categoryAdapterSelectedItem = savedInstanceState.getString(SAVED_STATE_NAVIGATION_ADAPTER_SLECTION);
@@ -165,6 +216,26 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
setupNotesList();
setupNavigationList(categoryAdapterSelectedItem);
setupNavigationMenu();
+
+ //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ //boolean ssoAnnouncmentShown = prefs.getBoolean("sp_sso_announchment_shown", false);
+ //if (!ssoAnnouncmentShown) {
+ // AlertDialog dialog = new AlertDialog.Builder(this)
+ // .setTitle(R.string.sso_announcment_title)
+ // .setCancelable(false)
+ // .setMessage(R.string.sso_announcment_message)
+ // .setNegativeButton(R.string.sso_announcment_more_info, (a, b) -> {
+ // startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/stefan-niedermann/nextcloud-notes/blob/master/SSO%20Announcment.md")));
+ // })
+ // .setPositiveButton(R.string.sso_announcment_understood, (a, b) -> {
+ // SharedPreferences.Editor editor = prefs.edit();
+ // editor.putBoolean("sp_sso_announchment_shown", true);
+ // editor.apply();
+ // })
+ // .show();
+ // dialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.fg_default));
+ // dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(getResources().getColor(R.color.fg_default));
+ //}
}
private void migrateUrl() {
@@ -240,13 +311,13 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
}
@Override
- public void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.syncState();
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(SAVED_STATE_NAVIGATION_SELECTION, navigationSelection);
outState.putString(SAVED_STATE_NAVIGATION_ADAPTER_SLECTION, adapterCategories.getSelectedItem());
@@ -289,13 +360,10 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
});
// Floating Action Button
- fabCreate.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent createIntent = new Intent(getApplicationContext(), EditNoteActivity.class);
- createIntent.putExtra(EditNoteActivity.PARAM_CATEGORY, navigationSelection);
- startActivityForResult(createIntent, create_note_cmd);
- }
+ fabCreate.setOnClickListener((View view) -> {
+ Intent createIntent = new Intent(getApplicationContext(), EditNoteActivity.class);
+ createIntent.putExtra(EditNoteActivity.PARAM_CATEGORY, navigationSelection);
+ startActivityForResult(createIntent, create_note_cmd);
});
}
@@ -469,12 +537,9 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
this.updateUsernameInDrawer();
final NotesListViewActivity that = this;
- this.account.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent settingsIntent = new Intent(that, SettingsActivity.class);
- startActivityForResult(settingsIntent, server_settings);
- }
+ this.headerView.setOnClickListener((View v) -> {
+ Intent settingsIntent = new Intent(that, SettingsActivity.class);
+ startActivityForResult(settingsIntent, server_settings);
});
adapterMenu.setItems(itemsMenu);
@@ -487,7 +552,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
listView.setLayoutManager(new LinearLayoutManager(this));
ItemTouchHelper touchHelper = new ItemTouchHelper(new SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@@ -499,7 +564,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
* @return 0 if section, otherwise super()
*/
@Override
- public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ public int getSwipeDirs(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof ItemAdapter.SectionViewHolder) return 0;
return super.getSwipeDirs(recyclerView, viewHolder);
}
@@ -511,8 +576,8 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
* @param direction int
*/
@Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
- switch(direction) {
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
+ switch (direction) {
case ItemTouchHelper.LEFT: {
final DBNote dbNote = (DBNote) adapter.getItem(viewHolder.getAdapterPosition());
db.deleteNoteAndSync((dbNote).getId());
@@ -520,14 +585,11 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
refreshLists();
Log.v("Note", "Item deleted through swipe ----------------------------------------------");
Snackbar.make(swipeRefreshLayout, R.string.action_note_deleted, Snackbar.LENGTH_LONG)
- .setAction(R.string.action_undo, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- db.addNoteAndSync(dbNote);
- refreshLists();
- Snackbar.make(swipeRefreshLayout, R.string.action_note_restored, Snackbar.LENGTH_SHORT)
- .show();
- }
+ .setAction(R.string.action_undo, (View v) -> {
+ db.addNoteAndSync(dbNote);
+ refreshLists();
+ Snackbar.make(swipeRefreshLayout, R.string.action_note_restored, Snackbar.LENGTH_SHORT)
+ .show();
})
.show();
break;
@@ -542,16 +604,16 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
}
@Override
- public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
+ public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
ItemAdapter.NoteViewHolder noteViewHolder = (ItemAdapter.NoteViewHolder) viewHolder;
// show swipe icon on the side
- noteViewHolder.showSwipe(dX>0);
+ noteViewHolder.showSwipe(dX > 0);
// move only swipeable part of item (not leave-behind)
getDefaultUIUtil().onDraw(c, recyclerView, noteViewHolder.noteSwipeable, dX, dY, actionState, isCurrentlyActive);
}
@Override
- public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
getDefaultUIUtil().clearView(((ItemAdapter.NoteViewHolder) viewHolder).noteSwipeable);
}
});
@@ -561,6 +623,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
private void refreshLists() {
refreshLists(false);
}
+
private void refreshLists(final boolean scrollToTop) {
String subtitle = "";
if (navigationSelection.category != null) {
@@ -580,14 +643,11 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
query = searchView.getQuery();
}
- LoadNotesListTask.NotesLoadedListener callback = new LoadNotesListTask.NotesLoadedListener() {
- @Override
- public void onNotesLoaded(List- notes, boolean showCategory) {
- adapter.setShowCategory(showCategory);
- adapter.setItemList(notes);
- if(scrollToTop) {
- listView.scrollToPosition(0);
- }
+ LoadNotesListTask.NotesLoadedListener callback = (List
- notes, boolean showCategory) -> {
+ adapter.setShowCategory(showCategory);
+ adapter.setItemList(notes);
+ if (scrollToTop) {
+ listView.scrollToPosition(0);
}
};
new LoadNotesListTask(getApplicationContext(), callback, navigationSelection, query).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
@@ -615,11 +675,12 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
final MenuItem item = menu.findItem(R.id.search);
searchView = (SearchView) item.getActionView();
- final LinearLayout searchEditFrame = searchView.findViewById(android.support.v7.appcompat.R.id
+ final LinearLayout searchEditFrame = searchView.findViewById(R.id
.search_edit_frame);
searchEditFrame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
int oldVisibility = -1;
+
@Override
public void onGlobalLayout() {
int currentVisibility = searchEditFrame.getVisibility();
@@ -628,11 +689,8 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
if (currentVisibility == View.VISIBLE) {
fabCreate.hide();
} else {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- fabCreate.show();
- }
+ new Handler().postDelayed(() -> {
+ fabCreate.show();
}, 150);
}
@@ -680,8 +738,17 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
if (resultCode == RESULT_OK) {
//not need because of db.synchronisation in createActivity
- DBNote createdNote = (DBNote) data.getExtras().getSerializable(CREATED_NOTE);
- adapter.add(createdNote);
+ Bundle bundle = data.getExtras();
+ if (bundle != null) {
+ DBNote createdNote = (DBNote) data.getExtras().getSerializable(CREATED_NOTE);
+ if (createdNote != null) {
+ adapter.add(createdNote);
+ } else {
+ Log.w(NotesListViewActivity.class.getSimpleName(), "createdNote is null");
+ }
+ } else {
+ Log.w(NotesListViewActivity.class.getSimpleName(), "bundle is null");
+ }
}
listView.scrollToPosition(0);
} else if (requestCode == login_account) {
@@ -725,9 +792,20 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
private void updateUsernameInDrawer() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String username = preferences.getString(SettingsActivity.SETTINGS_USERNAME, SettingsActivity.DEFAULT_SETTINGS);
- String url = preferences.getString(SettingsActivity.SETTINGS_URL, SettingsActivity.DEFAULT_SETTINGS).replace("https://", "").replace("http://", "");
- if(!SettingsActivity.DEFAULT_SETTINGS.equals(username) && !SettingsActivity.DEFAULT_SETTINGS.equals(url)) {
- this.account.setText(username + "@" + url.substring(0, url.length() - 1));
+ String url = preferences.getString(SettingsActivity.SETTINGS_URL, SettingsActivity.DEFAULT_SETTINGS);
+ if (url != null) {
+ String croppedUrl = url.replace("https://", "").replace("http://", "");
+ if (!SettingsActivity.DEFAULT_SETTINGS.equals(username) && !SettingsActivity.DEFAULT_SETTINGS.equals(url)) {
+ this.account.setText(username + "@" + croppedUrl.substring(0, croppedUrl.length() - 1));
+ Glide
+ .with(this)
+ .load(url + "/index.php/avatar/" + Uri.encode(username) + "/64")
+ .error(R.mipmap.ic_launcher)
+ .apply(RequestOptions.circleCropTransform())
+ .into(this.currentAccountImage);
+ }
+ } else {
+ Log.w(NotesListViewActivity.class.getSimpleName(), "url is null");
}
}
@@ -853,4 +931,4 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap
adapter.notifyDataSetChanged();
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/foundation/e/notes/android/activity/PreferencesActivity.java b/app/src/main/java/foundation/e/notes/android/activity/PreferencesActivity.java
index f317c56472ba181995d59fe4ebb3037fd31a926f..56beced70ed93edd9d0125612b713f46de43362c 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/PreferencesActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/PreferencesActivity.java
@@ -1,11 +1,12 @@
package foundation.e.notes.android.activity;
import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import foundation.e.notes.android.fragment.PreferencesFragment;
-import foundation.e.notes.android.fragment.PreferencesFragment;
+import foundation.e.notes.util.ExceptionHandler;
/**
* Allows to change application settings.
@@ -15,6 +16,7 @@ public class PreferencesActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
setResult(RESULT_CANCELED);
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new PreferencesFragment())
diff --git a/app/src/main/java/foundation/e/notes/android/activity/SelectSingleNoteActivity.java b/app/src/main/java/foundation/e/notes/android/activity/SelectSingleNoteActivity.java
index 67de196b084f1e732688cd4884894acdbc23a011..544d305c1817af7c9933297c79ad9448586bce98 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/SelectSingleNoteActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/SelectSingleNoteActivity.java
@@ -6,10 +6,11 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.support.v4.widget.SwipeRefreshLayout;
import android.view.Menu;
import android.view.View;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
@@ -18,6 +19,7 @@ import foundation.e.notes.model.DBNote;
import foundation.e.notes.model.Item;
import foundation.e.notes.model.ItemAdapter;
import foundation.e.notes.util.Notes;
+import foundation.e.notes.util.ExceptionHandler;
public class SelectSingleNoteActivity extends NotesListViewActivity {
@@ -27,6 +29,7 @@ public class SelectSingleNoteActivity extends NotesListViewActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
setResult(Activity.RESULT_CANCELED);
SwipeRefreshLayout swipeRefreshLayout = getSwipeRefreshLayout();
@@ -34,7 +37,7 @@ public class SelectSingleNoteActivity extends NotesListViewActivity {
ButterKnife.bind(this);
fabCreate.setVisibility(View.GONE);
- android.support.v7.app.ActionBar ab = getSupportActionBar();
+ androidx.appcompat.app.ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.activity_select_single_note);
}
diff --git a/app/src/main/java/foundation/e/notes/android/activity/SettingsActivity.java b/app/src/main/java/foundation/e/notes/android/activity/SettingsActivity.java
index 6d4318c0ee3b541c7c9401dc7fdddf68bc4c730f..04424c947fc97b20ffdb4992f50cf7d7d8184f87 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/SettingsActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/SettingsActivity.java
@@ -3,30 +3,56 @@ package foundation.e.notes.android.activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
+import android.net.http.SslCertificate;
+import android.net.http.SslError;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.preference.PreferenceManager;
-import android.support.design.widget.TextInputLayout;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import foundation.e.cert4android.CustomCertManager;
+import foundation.e.cert4android.IOnCertificateDecision;
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
import foundation.e.notes.persistence.NoteSQLiteOpenHelper;
import foundation.e.notes.persistence.NoteServerSyncHelper;
+import foundation.e.notes.util.ExceptionHandler;
import foundation.e.notes.util.NotesClientUtil;
import foundation.e.notes.util.NotesClientUtil.LoginStatus;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.ContextCompat;
+
+import com.google.android.material.snackbar.Snackbar;
+import com.google.android.material.textfield.TextInputLayout;
+
+import java.io.ByteArrayInputStream;
+import java.net.URLDecoder;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import static android.os.Process.killProcess;
+import static android.os.Process.myPid;
/**
* @author Nihar Thakkar
@@ -45,10 +71,15 @@ public class SettingsActivity extends AppCompatActivity {
public static final String DEFAULT_SETTINGS = "";
public static final int CREDENTIALS_CHANGED = 3;
+ public static final String LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":";
+ public static final String WEBDAV_PATH_4_0_AND_LATER = "/remote.php/webdav";
+
private SharedPreferences preferences = null;
@BindView(R.id.settings_url)
EditText field_url;
+ @BindView(R.id.settings_username_wrapper)
+ TextInputLayout username_wrapper;
@BindView(R.id.settings_username)
EditText field_username;
@BindView(R.id.settings_password)
@@ -61,11 +92,15 @@ public class SettingsActivity extends AppCompatActivity {
View urlWarnHttp;
private String old_password = "";
+ private WebView webView;
+
private boolean first_run = false;
+ private boolean useWebLogin = true;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
setContentView(R.layout.activity_settings);
ButterKnife.bind(this);
@@ -79,13 +114,38 @@ public class SettingsActivity extends AppCompatActivity {
}
}
+ setupListener();
+
+ // Load current Preferences
+ field_url.setText(preferences.getString(SETTINGS_URL, DEFAULT_SETTINGS));
+ field_username.setText(preferences.getString(SETTINGS_USERNAME, DEFAULT_SETTINGS));
+ old_password = preferences.getString(SETTINGS_PASSWORD, DEFAULT_SETTINGS);
+
+ field_password.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ login();
+ return true;
+ }
+ });
+ field_password.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ setPasswordHint(hasFocus);
+ }
+ });
+ setPasswordHint(false);
+
+ handleSubmitButtonEnabled();
+ }
+
+ private void setupListener() {
field_url.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
new URLValidatorAsyncTask().execute(NotesClientUtil.formatURL(field_url.getText().toString()));
}
});
-
field_url.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@@ -101,7 +161,7 @@ public class SettingsActivity extends AppCompatActivity {
urlWarnHttp.setVisibility(View.GONE);
}
- handleSubmitButtonEnabled(field_url.getText(), field_username.getText());
+ handleSubmitButtonEnabled();
}
@Override
@@ -117,7 +177,7 @@ public class SettingsActivity extends AppCompatActivity {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- handleSubmitButtonEnabled(field_url.getText(), field_username.getText());
+ handleSubmitButtonEnabled();
}
@Override
@@ -126,27 +186,6 @@ public class SettingsActivity extends AppCompatActivity {
}
});
- // Load current Preferences
- field_url.setText(preferences.getString(SETTINGS_URL, DEFAULT_SETTINGS));
- field_username.setText(preferences.getString(SETTINGS_USERNAME, DEFAULT_SETTINGS));
- old_password = preferences.getString(SETTINGS_PASSWORD, DEFAULT_SETTINGS);
-
- field_password.setOnEditorActionListener(new TextView.OnEditorActionListener() {
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- login();
- return true;
- }
- });
- field_password.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- setPasswordHint(hasFocus);
- }
- });
- setPasswordHint(false);
-
- btn_submit.setEnabled(false);
btn_submit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -172,7 +211,17 @@ public class SettingsActivity extends AppCompatActivity {
}
}
- private void login() {
+ /**
+ * Prevent pressing back button on first run
+ */
+ @Override
+ public void onBackPressed() {
+ if (!first_run) {
+ super.onBackPressed();
+ }
+ }
+
+ private void legacyLogin() {
String url = field_url.getText().toString().trim();
String username = field_username.getText().toString();
String password = field_password.getText().toString();
@@ -186,8 +235,206 @@ public class SettingsActivity extends AppCompatActivity {
new LoginValidatorAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, username, password);
}
- private void handleSubmitButtonEnabled(Editable url, Editable username) {
- if (field_username.getText().length() > 0 && field_url.getText().length() > 0) {
+ private void login() {
+ if (useWebLogin) {
+ webLogin();
+ } else {
+ legacyLogin();
+ }
+ }
+
+ /**
+ * Obtain the X509Certificate from SslError
+ *
+ * @param error SslError
+ * @return X509Certificate from error
+ */
+ public static X509Certificate getX509CertificateFromError(SslError error) {
+ Bundle bundle = SslCertificate.saveState(error.getCertificate());
+ X509Certificate x509Certificate;
+ byte[] bytes = bundle.getByteArray("x509-certificate");
+ if (bytes == null) {
+ x509Certificate = null;
+ } else {
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
+ x509Certificate = (X509Certificate) cert;
+ } catch (CertificateException e) {
+ x509Certificate = null;
+ }
+ }
+ return x509Certificate;
+ }
+
+ private void webLogin() {
+ setContentView(R.layout.activity_settings_webview);
+ webView = findViewById(R.id.login_webview);
+ webView.setVisibility(View.GONE);
+
+ final ProgressBar progressBar = findViewById(R.id.login_webview_progress_bar);
+
+ WebSettings settings = webView.getSettings();
+ settings.setAllowFileAccess(false);
+ settings.setJavaScriptEnabled(true);
+ settings.setDomStorageEnabled(true);
+ settings.setUserAgentString(getWebLoginUserAgent());
+ settings.setSaveFormData(false);
+ settings.setSavePassword(false);
+
+ Map headers = new HashMap<>();
+ headers.put("OCS-APIREQUEST", "true");
+
+
+ webView.loadUrl(normalizeUrlSuffix(NotesClientUtil.formatURL(field_url.getText().toString())) + "index.php/login/flow", headers);
+
+ webView.setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (url.startsWith("nc://login/")) {
+ parseAndLoginFromWebView(url);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+
+ progressBar.setVisibility(View.GONE);
+ webView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
+ X509Certificate cert = getX509CertificateFromError(error);
+
+ try {
+ final boolean[] accepted = new boolean[1];
+ NoteServerSyncHelper.getInstance(NoteSQLiteOpenHelper.getInstance(getApplicationContext()))
+ .checkCertificate(cert.getEncoded(), true, new IOnCertificateDecision.Stub() {
+ @Override
+ public void accept() {
+ Log.d("Note", "cert accepted");
+ handler.proceed();
+ accepted[0] = true;
+ }
+
+ @Override
+ public void reject() {
+ Log.d("Note", "cert rejected");
+ handler.cancel();
+ killProcess(myPid());
+ }
+ });
+ } catch (Exception e) {
+ Log.e("Note", "Cert could not be verified");
+ handler.proceed();
+ }
+ }
+
+ });
+
+ // show snackbar after 60s to switch back to old login method
+ new Handler().postDelayed(() -> {
+ Snackbar.make(webView, R.string.fallback_weblogin_text, Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.fallback_weblogin_back, (View.OnClickListener) v -> initLegacyLogin(field_url.getText().toString())).show();
+ }, 45 * 1000);
+ }
+
+ private String getWebLoginUserAgent() {
+ return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) +
+ Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL;
+ }
+
+ private void parseAndLoginFromWebView(String dataString) {
+ String prefix = "nc://login/";
+ LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString);
+
+ if (loginUrlInfo != null) {
+ String url = normalizeUrlSuffix(loginUrlInfo.serverAddress);
+
+ new LoginValidatorAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, loginUrlInfo.username,
+ loginUrlInfo.password);
+ }
+ }
+
+ /**
+ * parses a URI string and returns a login data object with the information from the URI string.
+ *
+ * @param prefix URI beginning, e.g. cloud://login/
+ * @param dataString the complete URI
+ * @return login data
+ * @throws IllegalArgumentException when
+ */
+ private LoginUrlInfo parseLoginDataUrl(String prefix, String dataString) throws IllegalArgumentException {
+ if (dataString.length() < prefix.length()) {
+ throw new IllegalArgumentException("Invalid login URL detected");
+ }
+ LoginUrlInfo loginUrlInfo = new LoginUrlInfo();
+
+ // format is basically xxx://login/server:xxx&user:xxx&password while all variables are optional
+ String data = dataString.substring(prefix.length());
+
+ // parse data
+ String[] values = data.split("&");
+
+ if (values.length < 1 || values.length > 3) {
+ // error illegal number of URL elements detected
+ throw new IllegalArgumentException("Illegal number of login URL elements detected: " + values.length);
+ }
+
+ for (String value : values) {
+ if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) {
+ loginUrlInfo.username = URLDecoder.decode(
+ value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()));
+ } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) {
+ loginUrlInfo.password = URLDecoder.decode(
+ value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()));
+ } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) {
+ loginUrlInfo.serverAddress = URLDecoder.decode(
+ value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length()));
+ } else {
+ // error illegal URL element detected
+ throw new IllegalArgumentException("Illegal magic login URL element detected: " + value);
+ }
+ }
+
+ return loginUrlInfo;
+ }
+
+ private String normalizeUrlSuffix(String url) {
+ if (url.toLowerCase(Locale.ROOT).endsWith(WEBDAV_PATH_4_0_AND_LATER)) {
+ return url.substring(0, url.length() - WEBDAV_PATH_4_0_AND_LATER.length());
+ }
+
+ if (!url.endsWith("/")) {
+ return url + "/";
+ }
+
+ return url;
+ }
+
+ private void initLegacyLogin(String oldUrl) {
+ useWebLogin = false;
+ new URLValidatorAsyncTask().execute(NotesClientUtil.formatURL(field_url.getText().toString()));
+
+ webView.setVisibility(View.INVISIBLE);
+ setContentView(R.layout.activity_settings);
+
+ ButterKnife.bind(this);
+ setupListener();
+
+ field_url.setText(oldUrl);
+ username_wrapper.setVisibility(View.VISIBLE);
+ password_wrapper.setVisibility(View.VISIBLE);
+ }
+
+ private void handleSubmitButtonEnabled() {
+ // drawable[2] is not null if url is valid, see URLValidatorAsyncTask::onPostExecute
+ if (useWebLogin || field_url.getCompoundDrawables()[2] != null && (username_wrapper.getVisibility() == View.GONE ||
+ (username_wrapper.getVisibility() == View.VISIBLE && field_username.getText().length() > 0))) {
btn_submit.setEnabled(true);
} else {
btn_submit.setEnabled(false);
@@ -204,6 +451,7 @@ public class SettingsActivity extends AppCompatActivity {
@Override
protected void onPreExecute() {
+ btn_submit.setEnabled(false);
field_url.setCompoundDrawables(null, null, null, null);
}
@@ -217,11 +465,12 @@ public class SettingsActivity extends AppCompatActivity {
protected void onPostExecute(Boolean o) {
if (o) {
Drawable actionDoneDark = ContextCompat.getDrawable(getApplicationContext(), R.drawable.ic_check_grey600_24dp);
- actionDoneDark.setBounds( 0, 0, actionDoneDark.getIntrinsicWidth(), actionDoneDark.getIntrinsicHeight() );
+ actionDoneDark.setBounds(0, 0, actionDoneDark.getIntrinsicWidth(), actionDoneDark.getIntrinsicHeight());
field_url.setCompoundDrawables(null, null, actionDoneDark, null);
} else {
field_url.setCompoundDrawables(null, null, null, null);
}
+ handleSubmitButtonEnabled();
}
}
@@ -286,4 +535,13 @@ public class SettingsActivity extends AppCompatActivity {
field_password.setEnabled(enabled);
}
}
+
+ /**
+ * Data object holding the login url fields.
+ */
+ public class LoginUrlInfo {
+ String serverAddress;
+ String username;
+ String password;
+ }
}
diff --git a/app/src/main/java/foundation/e/notes/android/activity/SplashscreenActivity.java b/app/src/main/java/foundation/e/notes/android/activity/SplashscreenActivity.java
index 7d1d9d6b53fcba3c203d0c45d67824b5e4b5fcfd..3b62f382ba5fd75670565af153f48443a8209c65 100644
--- a/app/src/main/java/foundation/e/notes/android/activity/SplashscreenActivity.java
+++ b/app/src/main/java/foundation/e/notes/android/activity/SplashscreenActivity.java
@@ -2,7 +2,10 @@ package foundation.e.notes.android.activity;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import foundation.e.notes.util.ExceptionHandler;
/**
* Created by stefan on 18.04.17.
@@ -12,9 +15,10 @@ public class SplashscreenActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this));
Intent intent = new Intent(this, NotesListViewActivity.class);
startActivity(intent);
finish();
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidget.java b/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidget.java
index 71620c5d4a4e1c124cb65e7538c5f4c84619ff90..ae172aab1fd19f372b39d3808c4cd926ecc324b7 100644
--- a/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidget.java
+++ b/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidget.java
@@ -9,13 +9,12 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
+import android.util.Log;
import android.widget.RemoteViews;
import foundation.e.notes.android.activity.EditNoteActivity;
import foundation.e.notes.android.activity.NotesListViewActivity;
import foundation.e.notes.R;
-import foundation.e.notes.android.activity.EditNoteActivity;
-import foundation.e.notes.android.activity.NotesListViewActivity;
public class NoteListWidget extends AppWidgetProvider {
public static final String WIDGET_MODE_KEY = "NLW_mode";
@@ -54,20 +53,20 @@ public class NoteListWidget extends AppWidgetProvider {
// Launch application when user taps the header icon or app title
Intent intent = new Intent("android.intent.action.MAIN");
intent.setComponent(new ComponentName(context.getPackageName(),
- NotesListViewActivity.class.getName()));
+ NotesListViewActivity.class.getName()));
// Open the main app if the user taps the widget header
- PendingIntent openAppI = PendingIntent.getActivity( context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent openAppI = PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
// Launch create note activity if user taps "+" icon on header
- PendingIntent newNoteI = PendingIntent.getActivity( context,0,
- (new Intent(context, EditNoteActivity.class)),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent newNoteI = PendingIntent.getActivity(context, 0,
+ (new Intent(context, EditNoteActivity.class)),
+ PendingIntent.FLAG_UPDATE_CURRENT);
- PendingIntent templatePI = PendingIntent.getActivity(context,0,
- (new Intent(context, EditNoteActivity.class)),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent templatePI = PendingIntent.getActivity(context, 0,
+ (new Intent(context, EditNoteActivity.class)),
+ PendingIntent.FLAG_UPDATE_CURRENT);
if (darkTheme) {
views = new RemoteViews(context.getPackageName(), foundation.e.notes.R.layout.widget_note_list_dark);
@@ -78,6 +77,7 @@ public class NoteListWidget extends AppWidgetProvider {
views.setPendingIntentTemplate(foundation.e.notes.R.id.note_list_widget_lv_dark, templatePI);
views.setRemoteAdapter(appWidgetId, foundation.e.notes.R.id.note_list_widget_lv_dark, serviceIntent);
views.setEmptyView(foundation.e.notes.R.id.note_list_widget_lv_dark, foundation.e.notes.R.id.widget_note_list_placeholder_tv_dark);
+ awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.note_list_widget_lv_dark);
} else {
views = new RemoteViews(context.getPackageName(), foundation.e.notes.R.layout.widget_note_list);
views.setTextViewText(foundation.e.notes.R.id.widget_note_list_title_tv, getWidgetTitle(context, displayMode, category));
@@ -87,6 +87,7 @@ public class NoteListWidget extends AppWidgetProvider {
views.setPendingIntentTemplate(foundation.e.notes.R.id.note_list_widget_lv, templatePI);
views.setRemoteAdapter(appWidgetId, foundation.e.notes.R.id.note_list_widget_lv, serviceIntent);
views.setEmptyView(foundation.e.notes.R.id.note_list_widget_lv, foundation.e.notes.R.id.widget_note_list_placeholder_tv);
+ awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.note_list_widget_lv);
}
awm.updateAppWidget(appWidgetId, views);
@@ -104,12 +105,20 @@ public class NoteListWidget extends AppWidgetProvider {
super.onReceive(context, intent);
AppWidgetManager awm = AppWidgetManager.getInstance(context);
- if (intent.getAction().equals(awm.ACTION_APPWIDGET_UPDATE)) {
- if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
- updateAppWidget(context, awm, new int[] { intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) });
- } else {
- updateAppWidget(context, awm, awm.getAppWidgetIds(new ComponentName(context, NoteListWidget.class)));
+ if (intent.getAction() != null) {
+ if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)) {
+ if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
+ if (intent.getExtras() != null) {
+ updateAppWidget(context, awm, new int[]{intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)});
+ } else {
+ Log.w(NoteListWidget.class.getSimpleName(), "intent.getExtras() is null");
+ }
+ } else {
+ updateAppWidget(context, awm, awm.getAppWidgetIds(new ComponentName(context, NoteListWidget.class)));
+ }
}
+ } else {
+ Log.w(NoteListWidget.class.getSimpleName(), "intent.getAction() is null");
}
}
@@ -129,10 +138,11 @@ public class NoteListWidget extends AppWidgetProvider {
}
private static String getWidgetTitle(Context context, int displayMode, String category) {
- switch (displayMode)
- {
- case NoteListWidget.NLW_DISPLAY_ALL: return context.getString(foundation.e.notes.R.string.app_name);
- case NoteListWidget.NLW_DISPLAY_STARRED: return context.getString(foundation.e.notes.R.string.label_favorites);
+ switch (displayMode) {
+ case NoteListWidget.NLW_DISPLAY_ALL:
+ return context.getString(R.string.app_name);
+ case NoteListWidget.NLW_DISPLAY_STARRED:
+ return context.getString(R.string.label_favorites);
case NoteListWidget.NLW_DISPLAY_CATEGORY:
if (category.equals("")) {
return context.getString(foundation.e.notes.R.string.action_uncategorized);
diff --git a/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidgetConfiguration.java b/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidgetConfiguration.java
index 93d129ffcc151a54f60c690a9c5b87340a449d5d..7b59340e118426d3f584ddd1e805bdde3f08cd4f 100644
--- a/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidgetConfiguration.java
+++ b/app/src/main/java/foundation/e/notes/android/appwidget/NoteListWidgetConfiguration.java
@@ -7,10 +7,10 @@ import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.support.annotation.Nullable;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.widget.Toast;
diff --git a/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidget.java b/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidget.java
index 114ffd9c3e6ee4a7b0bf54b021d5f6f97c6d7268..53ab25e345e42a678b21cfc4ecb282067243a750 100644
--- a/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidget.java
+++ b/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidget.java
@@ -13,7 +13,6 @@ import android.widget.RemoteViews;
import foundation.e.notes.android.activity.EditNoteActivity;
import foundation.e.notes.R;
-import foundation.e.notes.android.activity.EditNoteActivity;
public class SingleNoteWidget extends AppWidgetProvider {
private static boolean darkTheme;
@@ -48,11 +47,13 @@ public class SingleNoteWidget extends AppWidgetProvider {
views.setPendingIntentTemplate(foundation.e.notes.R.id.single_note_widget_lv_dark, templatePendingIntent);
views.setRemoteAdapter(foundation.e.notes.R.id.single_note_widget_lv_dark, serviceIntent);
views.setEmptyView(foundation.e.notes.R.id.single_note_widget_lv_dark, foundation.e.notes.R.id.widget_single_note_placeholder_tv_dark);
+ awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv_dark);
} else {
views = new RemoteViews(context.getPackageName(), foundation.e.notes.R.layout.widget_single_note);
views.setPendingIntentTemplate(foundation.e.notes.R.id.single_note_widget_lv, templatePendingIntent);
views.setRemoteAdapter(foundation.e.notes.R.id.single_note_widget_lv, serviceIntent);
views.setEmptyView(foundation.e.notes.R.id.single_note_widget_lv, foundation.e.notes.R.id.widget_single_note_placeholder_tv);
+ awm.notifyAppWidgetViewDataChanged(appWidgetId, R.id.single_note_widget_lv);
}
awm.updateAppWidget(appWidgetId, views);
diff --git a/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidgetFactory.java b/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidgetFactory.java
index b4275f7d0df049361d93e34cd82766614932ad8c..ead58014ddc424b2757de21f6d810505de0c7eca 100644
--- a/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidgetFactory.java
+++ b/app/src/main/java/foundation/e/notes/android/appwidget/SingleNoteWidgetFactory.java
@@ -34,14 +34,13 @@ public class SingleNoteWidgetFactory implements RemoteViewsService.RemoteViewsFa
SingleNoteWidgetFactory(Context context, Intent intent) {
this.context = context;
- markdownProcessor = new MarkdownProcessor(this.context);
- markdownProcessor.factory(TextFactory.create());
- markdownProcessor.config(MarkDownUtil.getMarkDownConfiguration(this.context).build());
appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
- AppWidgetManager.INVALID_APPWIDGET_ID);
-
+ AppWidgetManager.INVALID_APPWIDGET_ID);
sp = PreferenceManager.getDefaultSharedPreferences(this.context);
darkTheme = sp.getBoolean(SingleNoteWidget.DARK_THEME_KEY + appWidgetId, false);
+ markdownProcessor = new MarkdownProcessor(this.context);
+ markdownProcessor.factory(TextFactory.create());
+ markdownProcessor.config(MarkDownUtil.getMarkDownConfiguration(this.context, darkTheme).build());
}
@Override
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/BaseNoteFragment.java b/app/src/main/java/foundation/e/notes/android/fragment/BaseNoteFragment.java
index 13ace354baa9b532a145d34e12b05cfd752a278e..b3536b0790b32a6760d0f54b3cf29250bae81747 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/BaseNoteFragment.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/BaseNoteFragment.java
@@ -3,22 +3,41 @@ package foundation.e.notes.android.fragment;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
+import android.app.PendingIntent;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.ShareActionProvider;
+import android.text.SpannableString;
+import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.SearchView;
+import androidx.appcompat.widget.ShareActionProvider;
+import androidx.core.view.MenuItemCompat;
+import androidx.core.view.ViewCompat;
import foundation.e.notes.R;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.model.DBNote;
import foundation.e.notes.persistence.NoteSQLiteOpenHelper;
import foundation.e.notes.util.ICallback;
+import foundation.e.notes.android.activity.EditNoteActivity;
+import foundation.e.notes.util.DisplayUtils;
+
+import static androidx.core.content.pm.ShortcutManagerCompat.isRequestPinShortcutSupported;
+import static foundation.e.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT;
public abstract class BaseNoteFragment extends Fragment implements CategoryDialogFragment.CategoryDialogListener {
@@ -28,21 +47,45 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
void onNoteUpdated(DBNote note);
}
+ private static final int MENU_ID_PIN = -1;
public static final String PARAM_NOTE_ID = "noteId";
public static final String PARAM_NEWNOTE = "newNote";
private static final String SAVEDKEY_NOTE = "note";
private static final String SAVEDKEY_ORIGINAL_NOTE = "original_note";
+ protected SearchView searchView;
+ protected MenuItem searchMenuItem;
+
+ protected String searchQuery = null;
+
protected DBNote note;
@Nullable
private DBNote originalNote;
private NoteSQLiteOpenHelper db;
private NoteFragmentListener listener;
+ private TextView activeTextView;
+ private boolean isNew;
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ searchQuery = savedInstanceState.getString("searchQuery", "");
+ }
+
+ }
+
+ protected void setActiveTextView(TextView textView) {
+ activeTextView = textView;
+ }
+
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
+ isNew = true;
long id = getArguments().getLong(PARAM_NOTE_ID);
if (id > 0) {
note = originalNote = db.getNote(id);
@@ -55,6 +98,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
originalNote = null;
}
} else {
+ isNew = false;
note = (DBNote) savedInstanceState.getSerializable(SAVEDKEY_NOTE);
originalNote = (DBNote) savedInstanceState.getSerializable(SAVEDKEY_ORIGINAL_NOTE);
}
@@ -96,11 +140,27 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
saveNote(null);
outState.putSerializable(SAVEDKEY_NOTE, note);
outState.putSerializable(SAVEDKEY_ORIGINAL_NOTE, originalNote);
+
+ if (searchView != null && !TextUtils.isEmpty(searchView.getQuery().toString())) {
+ outState.putString("searchQuery", searchView.getQuery().toString());
+ }
+ }
+
+ private void colorWithText(String newText) {
+ if (activeTextView != null && ViewCompat.isAttachedToWindow(activeTextView)) {
+ activeTextView.setText(DisplayUtils.searchAndColor(activeTextView.getText().toString(), new SpannableString
+ (activeTextView.getText()), newText, getResources().getColor(R.color.primary)),
+ TextView.BufferType.SPANNABLE);
+ }
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_note_fragment, menu);
+
+ if (isRequestPinShortcutSupported(getActivity()) && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ menu.add(Menu.NONE, MENU_ID_PIN, 110, R.string.pin_to_homescreen);
+ }
}
@Override
@@ -108,6 +168,55 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
super.onPrepareOptionsMenu(menu);
MenuItem itemFavorite = menu.findItem(R.id.menu_favorite);
prepareFavoriteOption(itemFavorite);
+
+ searchMenuItem = menu.findItem(R.id.search);
+ searchView = (SearchView) searchMenuItem.getActionView();
+
+ if (!TextUtils.isEmpty(searchQuery) && isNew) {
+ searchMenuItem.expandActionView();
+ searchView.setQuery(searchQuery, true);
+ searchView.clearFocus();
+ } else {
+ searchMenuItem.collapseActionView();
+ }
+
+
+ final LinearLayout searchEditFrame = searchView.findViewById(R.id
+ .search_edit_frame);
+
+ searchEditFrame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ int oldVisibility = -1;
+
+ @Override
+ public void onGlobalLayout() {
+ int currentVisibility = searchEditFrame.getVisibility();
+
+ if (currentVisibility != oldVisibility) {
+ if (currentVisibility != View.VISIBLE) {
+ colorWithText("");
+ searchQuery = "";
+ }
+
+ oldVisibility = currentVisibility;
+ }
+ }
+
+ });
+
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ searchQuery = newText;
+ colorWithText(newText);
+ return true;
+ }
+ });
+
}
private void prepareFavoriteOption(MenuItem item) {
@@ -157,6 +266,33 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
}
return false;
+ case MENU_ID_PIN:
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ShortcutManager shortcutManager = getActivity().getSystemService(ShortcutManager.class);
+
+ if (shortcutManager.isRequestPinShortcutSupported()) {
+ Intent intent = new Intent(getActivity(), EditNoteActivity.class);
+ intent.putExtra(EditNoteActivity.PARAM_NOTE_ID, note.getId());
+ intent.setAction(ACTION_SHORTCUT);
+
+ ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(getActivity(), note.getId() + "")
+ .setShortLabel(note.getTitle())
+ .setIcon(Icon.createWithResource(getActivity().getApplicationContext(), note.isFavorite() ? R.drawable.ic_star_yellow_24dp : R.drawable.ic_star_grey_ccc_24dp))
+ .setIntent(intent)
+ .build();
+
+ Intent pinnedShortcutCallbackIntent =
+ shortcutManager.createShortcutResultIntent(pinShortcutInfo);
+
+ PendingIntent successCallback = PendingIntent.getBroadcast(getActivity(), /* request code */ 0,
+ pinnedShortcutCallbackIntent, /* flags */ 0);
+
+ shortcutManager.requestPinShortcut(pinShortcutInfo,
+ successCallback.getIntentSender());
+ }
+ }
+
+ return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -176,7 +312,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
protected void saveNote(@Nullable ICallback callback) {
Log.d(getClass().getSimpleName(), "saveData()");
String newContent = getContent();
- if(note.getContent().equals(newContent)) {
+ if (note.getContent().equals(newContent)) {
Log.v(getClass().getSimpleName(), "... not saving, since nothing has changed");
} else {
note = db.updateNoteAndSync(note, newContent, callback);
@@ -184,6 +320,21 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo
}
}
+ protected float getFontSizeFromPreferences(SharedPreferences sp) {
+ final String prefValueSmall = getString(R.string.pref_value_font_size_small);
+ final String prefValueMedium = getString(R.string.pref_value_font_size_medium);
+ final String prefValueLarge = getString(R.string.pref_value_font_size_large);
+ String fontSize = sp.getString(getString(R.string.pref_key_font_size), prefValueMedium);
+
+ if (fontSize.equals(prefValueSmall)) {
+ return getResources().getDimension(R.dimen.note_font_size_small);
+ } else if (fontSize.equals(prefValueMedium)) {
+ return getResources().getDimension(R.dimen.note_font_size_medium);
+ } else {
+ return getResources().getDimension(R.dimen.note_font_size_large);
+ }
+ }
+
protected abstract String getContent();
/**
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/CategoryDialogFragment.java b/app/src/main/java/foundation/e/notes/android/fragment/CategoryDialogFragment.java
index 9122a171a7698bd77f81d0c1d96893728ea52230..6448e31dea9e9115a83a547d158f66752bd54022 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/CategoryDialogFragment.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/CategoryDialogFragment.java
@@ -8,7 +8,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.annotation.NonNull;
+import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
@@ -17,6 +17,7 @@ import android.widget.Filter;
import java.util.ArrayList;
import java.util.List;
+import androidx.annotation.NonNull;
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
@@ -68,7 +69,7 @@ public class CategoryDialogFragment extends DialogFragment {
public void onClick(DialogInterface dialog, int which) {
CategoryDialogListener listener;
Fragment target = getTargetFragment();
- if (target != null && target instanceof CategoryDialogListener) {
+ if (target instanceof CategoryDialogListener) {
listener = (CategoryDialogListener) target;
} else {
listener = (CategoryDialogListener) getActivity();
@@ -88,7 +89,11 @@ public class CategoryDialogFragment extends DialogFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ if (getDialog().getWindow() != null) {
+ getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ } else {
+ Log.w(CategoryDialogFragment.class.getSimpleName(), "can not set SOFT_INPUT_STATE_ALWAYAS_VISIBLE because getWindow() == null");
+ }
}
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/NoteEditFragment.java b/app/src/main/java/foundation/e/notes/android/fragment/NoteEditFragment.java
index 3e858af666fcd4c7c58fbc1422032bd6fae5ee5b..3d3f9785baab3864021b5e3d683d692552d40a03 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/NoteEditFragment.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/NoteEditFragment.java
@@ -1,19 +1,26 @@
package foundation.e.notes.android.fragment;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.support.annotation.Nullable;
+import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.yydcdut.markdown.syntax.edit.EditFactory;
import com.yydcdut.rxmarkdown.RxMDEditText;
import com.yydcdut.rxmarkdown.RxMarkdown;
@@ -24,6 +31,7 @@ import foundation.e.notes.R;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.util.ICallback;
import foundation.e.notes.util.MarkDownUtil;
+import foundation.e.notes.util.StyleCallback;
import rx.Subscriber;
public class NoteEditFragment extends BaseNoteFragment {
@@ -32,12 +40,39 @@ public class NoteEditFragment extends BaseNoteFragment {
private static final long DELAY = 2000; // Wait for this time after typing before saving
private static final long DELAY_AFTER_SYNC = 5000; // Wait for this time after saving before checking for next save
-
+ @BindView(R.id.editContent)
+ RxMDEditText editContent;
private Handler handler;
private boolean saveActive, unsavedEdit;
+ private final Runnable runAutoSave = new Runnable() {
+ @Override
+ public void run() {
+ if (unsavedEdit) {
+ Log.d(LOG_TAG_AUTOSAVE, "runAutoSave: start AutoSave");
+ autoSave();
+ } else {
+ Log.d(LOG_TAG_AUTOSAVE, "runAutoSave: nothing changed");
+ }
+ }
+ };
+ private final TextWatcher textWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
- @BindView(R.id.editContent)
- RxMDEditText editContent;
+ @Override
+ public void onTextChanged(final CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(final Editable s) {
+ unsavedEdit = true;
+ if (!saveActive) {
+ handler.removeCallbacks(runAutoSave);
+ handler.postDelayed(runAutoSave, DELAY);
+ }
+ }
+ };
public static NoteEditFragment newInstance(long noteId) {
NoteEditFragment f = new NoteEditFragment();
@@ -78,56 +113,57 @@ public class NoteEditFragment extends BaseNoteFragment {
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- ButterKnife.bind(this, getView());
+ if(getView() != null) {
+ ButterKnife.bind(this, getView());
- if (note.getContent().isEmpty()) {
- getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- }
+ setActiveTextView(editContent);
- // workaround for issue yydcdut/RxMarkdown#41
- note.setContent(note.getContent().replace("\r\n", "\n"));
-
- editContent.setText(note.getContent());
- editContent.setEnabled(true);
-
- RxMarkdown.live(editContent)
- .config(MarkDownUtil.getMarkDownConfiguration(getActivity().getApplicationContext()).build())
- .factory(EditFactory.create())
- .intoObservable()
- .subscribe(new Subscriber() {
- @Override
- public void onCompleted() {
- }
-
- @Override
- public void onError(Throwable e) {
- }
-
- @Override
- public void onNext(CharSequence charSequence) {
- editContent.setText(charSequence, TextView.BufferType.SPANNABLE);
- }
- });
- }
+ if (note.getContent().isEmpty()) {
+ editContent.requestFocus();
- private final TextWatcher textWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
+ getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- @Override
- public void onTextChanged(final CharSequence s, int start, int before, int count) {
- }
+ InputMethodManager imm = (InputMethodManager)
+ getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(getView(), InputMethodManager.SHOW_IMPLICIT);
- @Override
- public void afterTextChanged(final Editable s) {
- unsavedEdit = true;
- if (!saveActive) {
- handler.removeCallbacks(runAutoSave);
- handler.postDelayed(runAutoSave, DELAY);
}
+
+ // workaround for issue yydcdut/RxMarkdown#41
+ note.setContent(note.getContent().replace("\r\n", "\n"));
+
+ editContent.setText(note.getContent());
+ editContent.setEnabled(true);
+
+ RxMarkdown.live(editContent)
+ .config(MarkDownUtil.getMarkDownConfiguration(editContent.getContext()).build())
+ .factory(EditFactory.create())
+ .intoObservable()
+ .subscribe(new Subscriber() {
+ @Override
+ public void onCompleted() {
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ }
+
+ @Override
+ public void onNext(CharSequence charSequence) {
+ editContent.setText(charSequence, TextView.BufferType.SPANNABLE);
+ }
+ });
+
+ editContent.setCustomSelectionActionModeCallback(new StyleCallback(this.editContent));
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
+ editContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(sp));
+ if (sp.getBoolean(getString(R.string.pref_key_font), false)) {
+ editContent.setTypeface(Typeface.MONOSPACE);
+ }
+ } else {
+ Log.e(NoteEditFragment.class.getSimpleName(), "getView() is null");
}
- };
+ }
@Override
public void onResume() {
@@ -142,18 +178,6 @@ public class NoteEditFragment extends BaseNoteFragment {
cancelTimers();
}
- private final Runnable runAutoSave = new Runnable() {
- @Override
- public void run() {
- if (unsavedEdit) {
- Log.d(LOG_TAG_AUTOSAVE, "runAutoSave: start AutoSave");
- autoSave();
- } else {
- Log.d(LOG_TAG_AUTOSAVE, "runAutoSave: nothing changed");
- }
- }
- };
-
private void cancelTimers() {
handler.removeCallbacks(runAutoSave);
}
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/NotePreviewFragment.java b/app/src/main/java/foundation/e/notes/android/fragment/NotePreviewFragment.java
index 07879043e8776bc5593fd51373af043f50099ad3..b9f3f3246fba89a8de0c36a115de6e400e24409c 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/NotePreviewFragment.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/NotePreviewFragment.java
@@ -1,9 +1,12 @@
package foundation.e.notes.android.fragment;
+import android.content.SharedPreferences;
+import android.graphics.Typeface;
import android.os.Bundle;
-import android.support.annotation.Nullable;
+import android.preference.PreferenceManager;
import android.text.method.LinkMovementMethod;
import android.util.Log;
+import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
@@ -14,6 +17,7 @@ import com.yydcdut.markdown.syntax.text.TextFactory;
import com.yydcdut.rxmarkdown.RxMDTextView;
import com.yydcdut.rxmarkdown.RxMarkdown;
+import androidx.annotation.Nullable;
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
@@ -53,24 +57,26 @@ public class NotePreviewFragment extends BaseNoteFragment {
super.onActivityCreated(savedInstanceState);
ButterKnife.bind(this, getView());
+ setActiveTextView(noteContent);
+
String content = note.getContent();
RxMarkdown.with(content, getActivity())
.config(
- MarkDownUtil.getMarkDownConfiguration(getActivity().getApplicationContext())
- /*.setOnTodoClickCallback(new OnTodoClickCallback() {
- @Override
- public CharSequence onTodoClicked(View view, String line, int lineNumber) {
- String[] lines = TextUtils.split(note.getContent(), "\\r?\\n");
- if(lines.length >= lineNumber) {
- lines[lineNumber] = line;
+ MarkDownUtil.getMarkDownConfiguration(noteContent.getContext())
+ /*.setOnTodoClickCallback(new OnTodoClickCallback() {
+ @Override
+ public CharSequence onTodoClicked(View view, String line, int lineNumber) {
+ String[] lines = TextUtils.split(note.getContent(), "\\r?\\n");
+ if(lines.length >= lineNumber) {
+ lines[lineNumber] = line;
+ }
+ noteContent.setText(TextUtils.join("\n", lines), TextView.BufferType.SPANNABLE);
+ saveNote(null);
+ return line;
+ }
}
- noteContent.setText(TextUtils.join("\n", lines), TextView.BufferType.SPANNABLE);
- saveNote(null);
- return line;
- }
- }
- )*/.build()
+ )*/.build()
)
.factory(TextFactory.create())
.intoObservable()
@@ -93,6 +99,12 @@ public class NotePreviewFragment extends BaseNoteFragment {
});
noteContent.setText(content);
noteContent.setMovementMethod(LinkMovementMethod.getInstance());
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
+ noteContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(sp));
+ if (sp.getBoolean(getString(R.string.pref_key_font), false)) {
+ noteContent.setTypeface(Typeface.MONOSPACE);
+ }
}
@Override
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/PreferencesFragment.java b/app/src/main/java/foundation/e/notes/android/fragment/PreferencesFragment.java
index 2f29856144eca3bf1e0f423dbce2c0b1e0cb7b3f..7e3cb8c0f70215bc88487783cd07b4f136dbaa55 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/PreferencesFragment.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/PreferencesFragment.java
@@ -1,18 +1,17 @@
package foundation.e.notes.android.fragment;
import android.app.Activity;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
-import android.preference.PreferenceManager;
import android.preference.SwitchPreference;
-import android.support.annotation.Nullable;
+import android.util.Log;
import android.widget.Toast;
import foundation.e.cert4android.CustomCertManager;
import foundation.e.notes.R;
import foundation.e.notes.util.Notes;
+import androidx.annotation.Nullable;
public class PreferencesFragment extends PreferenceFragment {
@Override
@@ -21,38 +20,26 @@ public class PreferencesFragment extends PreferenceFragment {
addPreferencesFromResource(R.xml.preferences);
Preference resetTrust = findPreference(getString(R.string.pref_key_reset_trust));
- resetTrust.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- CustomCertManager.Companion.resetCertificates(getActivity());
- Toast.makeText(getActivity(), getString(R.string.settings_cert_reset_toast), Toast.LENGTH_SHORT).show();
- return true;
- }
+ resetTrust.setOnPreferenceClickListener((Preference preference) -> {
+ CustomCertManager.Companion.resetCertificates(getActivity());
+ Toast.makeText(getActivity(), getString(R.string.settings_cert_reset_toast), Toast.LENGTH_SHORT).show();
+ return true;
});
final SwitchPreference themePref = (SwitchPreference) findPreference(getString(R.string.pref_key_theme));
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
- Boolean darkTheme = sp.getBoolean(getString(R.string.pref_key_theme), false);
-
- setThemePreferenceSummary(themePref, darkTheme);
- themePref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- Boolean darkTheme = (Boolean) newValue;
- Notes.setAppTheme(darkTheme);
- getActivity().setResult(Activity.RESULT_OK);
- getActivity().finish();
-
- return true;
- }
+ themePref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> {
+ Boolean darkTheme = (Boolean) newValue;
+ Notes.setAppTheme(darkTheme);
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().recreate();
+ return true;
});
- }
- private void setThemePreferenceSummary(SwitchPreference themePref, Boolean darkTheme) {
- if (darkTheme) {
- themePref.setSummary(getString(R.string.pref_value_theme_dark));
- } else {
- themePref.setSummary(getString(R.string.pref_value_theme_light));
- }
+ final SwitchPreference wifiOnlyPref = (SwitchPreference) findPreference(getString(R.string.pref_key_wifi_only));
+ wifiOnlyPref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> {
+ Boolean syncOnWifiOnly = (Boolean) newValue;
+ Log.v("Notes", "syncOnWifiOnly: " + syncOnWifiOnly);
+ return true;
+ });
}
}
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentContributingTab.java b/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentContributingTab.java
index 17687a19d7c987c50d59fb02cb85743ea5d55cfb..4aec91cb0fe1307698c94b08a4452e110965f588 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentContributingTab.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentContributingTab.java
@@ -1,12 +1,13 @@
package foundation.e.notes.android.fragment.about;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
@@ -22,7 +23,7 @@ public class AboutFragmentContributingTab extends Fragment {
TextView aboutTranslate;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_about_contribution_tab, container, false);
ButterKnife.bind(this, v);
SupportUtil.setHtml(aboutSource, R.string.about_source, getString(R.string.url_source));
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentCreditsTab.java b/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentCreditsTab.java
index b1b0df24968a7c94a502b963881f9f14ca6ee397..35b896e780b772d71c7b93f74c604754d931d061 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentCreditsTab.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentCreditsTab.java
@@ -1,12 +1,13 @@
package foundation.e.notes.android.fragment.about;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.BuildConfig;
@@ -23,7 +24,7 @@ public class AboutFragmentCreditsTab extends Fragment {
TextView aboutTranslators;
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_about_credits_tab, container, false);
ButterKnife.bind(this, v);
SupportUtil.setHtml(aboutVersion, R.string.about_version, "v" + BuildConfig.VERSION_NAME);
diff --git a/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentLicenseTab.java b/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentLicenseTab.java
index 8fbc445ceab1144af99cc36af441fc63ffb13a20..eac1b7374a5ae4779302e6466f7def31159b97bf 100644
--- a/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentLicenseTab.java
+++ b/app/src/main/java/foundation/e/notes/android/fragment/about/AboutFragmentLicenseTab.java
@@ -3,13 +3,14 @@ package foundation.e.notes.android.fragment.about;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
@@ -29,7 +30,7 @@ public class AboutFragmentLicenseTab extends Fragment {
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_about_license_tab, container, false);
ButterKnife.bind(this, v);
SupportUtil.setHtml(iconsDisclaimer, R.string.about_icons_disclaimer, getString(R.string.about_app_icon_author));
diff --git a/app/src/main/java/foundation/e/notes/android/providers/AppContentProvider.java b/app/src/main/java/foundation/e/notes/android/providers/AppContentProvider.java
index 093c332da9e7ffa79ec1f3b15d502bf241035103..30628bf0330f5296aa1490f30d80624bb1f359bb 100644
--- a/app/src/main/java/foundation/e/notes/android/providers/AppContentProvider.java
+++ b/app/src/main/java/foundation/e/notes/android/providers/AppContentProvider.java
@@ -4,8 +4,8 @@ import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
/**
* @author Nihar Thakkar
diff --git a/app/src/main/java/foundation/e/notes/android/quicksettings/NewNoteTileService.java b/app/src/main/java/foundation/e/notes/android/quicksettings/NewNoteTileService.java
index b15835521d15663e79af465b557dc6f17e84f756..9c17120e7edbde00543a35b65e6c130f4b4ee7c1 100644
--- a/app/src/main/java/foundation/e/notes/android/quicksettings/NewNoteTileService.java
+++ b/app/src/main/java/foundation/e/notes/android/quicksettings/NewNoteTileService.java
@@ -28,15 +28,8 @@ public class NewNoteTileService extends TileService {
// create new note intent
final Intent newNoteIntent = new Intent(getApplicationContext(), EditNoteActivity.class);
// ensure it won't open twice if already running
- newNoteIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
-
+ newNoteIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// ask to unlock the screen if locked, then start new note intent
- unlockAndRun(new Runnable() {
- @Override
- public void run() {
- startActivityAndCollapse(newNoteIntent);
- }
- });
-
+ unlockAndRun(() -> startActivityAndCollapse(newNoteIntent));
}
}
diff --git a/app/src/main/java/foundation/e/notes/model/Category.java b/app/src/main/java/foundation/e/notes/model/Category.java
index 3c60638458b3683ad0a366a39f7b555157704d80..861c24027dcea32483678b27541a65b6c01f9799 100644
--- a/app/src/main/java/foundation/e/notes/model/Category.java
+++ b/app/src/main/java/foundation/e/notes/model/Category.java
@@ -1,6 +1,6 @@
package foundation.e.notes.model;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
import java.io.Serializable;
diff --git a/app/src/main/java/foundation/e/notes/model/ItemAdapter.java b/app/src/main/java/foundation/e/notes/model/ItemAdapter.java
index 7be1df91a53f1375360764da2c556febdf622285..15963a9e906a7a084ae1fe330a24f46a20781401 100644
--- a/app/src/main/java/foundation/e/notes/model/ItemAdapter.java
+++ b/app/src/main/java/foundation/e/notes/model/ItemAdapter.java
@@ -1,7 +1,7 @@
package foundation.e.notes.model;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
@@ -16,7 +16,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import foundation.e.notes.R;
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
+import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
public class ItemAdapter extends RecyclerView.Adapter {
diff --git a/app/src/main/java/foundation/e/notes/model/NavigationAdapter.java b/app/src/main/java/foundation/e/notes/model/NavigationAdapter.java
index 65e2f8500f27e54a41433b43fafe59435f8a38ce..203c71e2e6ee195f5edd9b48e53d067919e41e04 100644
--- a/app/src/main/java/foundation/e/notes/model/NavigationAdapter.java
+++ b/app/src/main/java/foundation/e/notes/model/NavigationAdapter.java
@@ -1,10 +1,10 @@
package foundation.e.notes.model;
import android.graphics.Color;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/foundation/e/notes/persistence/LoadNotesListTask.java b/app/src/main/java/foundation/e/notes/persistence/LoadNotesListTask.java
index 843e23cac67bd94bfe0f69edd6a3fe4e5944116f..c7df2e93ae0c152720123055390aeea918de7fe6 100644
--- a/app/src/main/java/foundation/e/notes/persistence/LoadNotesListTask.java
+++ b/app/src/main/java/foundation/e/notes/persistence/LoadNotesListTask.java
@@ -2,25 +2,22 @@ package foundation.e.notes.persistence;
import android.content.Context;
import android.os.AsyncTask;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
import android.text.Html;
import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.text.style.ForegroundColorSpan;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import foundation.e.notes.model.Category;
-import foundation.e.notes.model.DBNote;
-import foundation.e.notes.model.Item;
-import foundation.e.notes.model.SectionItem;
-import foundation.e.notes.util.NoteUtil;
import foundation.e.notes.R;
import foundation.e.notes.model.Category;
import foundation.e.notes.model.DBNote;
@@ -34,6 +31,7 @@ public class LoadNotesListTask extends AsyncTask> {
private final NotesLoadedListener callback;
private final Category category;
private final CharSequence searchQuery;
+
public LoadNotesListTask(@NonNull Context context, @NonNull NotesLoadedListener callback, @NonNull Category category, @Nullable CharSequence searchQuery) {
this.context = context;
this.callback = callback;
@@ -65,15 +63,6 @@ public class LoadNotesListTask extends AsyncTask> {
dbNote.setTitle(Html.toHtml(spannableString));
- spannableString = new SpannableString(dbNote.getCategory());
- matcher = Pattern.compile("(" + searchQuery + ")", Pattern.CASE_INSENSITIVE).matcher(spannableString);
- while (matcher.find()) {
- spannableString.setSpan(new ForegroundColorSpan(context.getResources().getColor(foundation.e.notes.R.color.primary_dark)),
- matcher.start(), matcher.end(), 0);
- }
-
- dbNote.setCategory(Html.toHtml(spannableString));
-
spannableString = new SpannableString(dbNote.getExcerpt());
matcher = Pattern.compile("(" + searchQuery + ")", Pattern.CASE_INSENSITIVE).matcher(spannableString);
while (matcher.find()) {
@@ -107,68 +96,16 @@ public class LoadNotesListTask extends AsyncTask> {
@WorkerThread
private List
- fillListByTime(@NonNull List noteList) {
List
- itemList = new ArrayList<>();
- // #12 Create Sections depending on Time
- boolean todaySet, yesterdaySet, weekSet, monthSet, earlierSet;
- todaySet = yesterdaySet = weekSet = monthSet = earlierSet = false;
- Calendar today = Calendar.getInstance();
- today.set(Calendar.HOUR_OF_DAY, 0);
- today.set(Calendar.MINUTE, 0);
- today.set(Calendar.SECOND, 0);
- today.set(Calendar.MILLISECOND, 0);
- Calendar yesterday = Calendar.getInstance();
- yesterday.set(Calendar.DAY_OF_YEAR, yesterday.get(Calendar.DAY_OF_YEAR) - 1);
- yesterday.set(Calendar.HOUR_OF_DAY, 0);
- yesterday.set(Calendar.MINUTE, 0);
- yesterday.set(Calendar.SECOND, 0);
- yesterday.set(Calendar.MILLISECOND, 0);
- Calendar week = Calendar.getInstance();
- week.set(Calendar.DAY_OF_WEEK, week.getFirstDayOfWeek());
- week.set(Calendar.HOUR_OF_DAY, 0);
- week.set(Calendar.MINUTE, 0);
- week.set(Calendar.SECOND, 0);
- week.set(Calendar.MILLISECOND, 0);
- Calendar month = Calendar.getInstance();
- month.set(Calendar.DAY_OF_MONTH, 0);
- month.set(Calendar.HOUR_OF_DAY, 0);
- month.set(Calendar.MINUTE, 0);
- month.set(Calendar.SECOND, 0);
- month.set(Calendar.MILLISECOND, 0);
+ Timeslotter timeslotter = new Timeslotter();
+ String lastTimeslot = null;
for (int i = 0; i < noteList.size(); i++) {
DBNote currentNote = noteList.get(i);
- if (currentNote.isFavorite()) {
- // don't show as new section
- } else if (!todaySet && currentNote.getModified().getTimeInMillis() >= today.getTimeInMillis()) {
- // after 00:00 today
- if (i > 0) {
- itemList.add(new SectionItem(context.getResources().getString(foundation.e.notes.R.string.listview_updated_today)));
- }
- todaySet = true;
- } else if (!yesterdaySet && currentNote.getModified().getTimeInMillis() < today.getTimeInMillis() && currentNote.getModified().getTimeInMillis() >= yesterday.getTimeInMillis()) {
- // between today 00:00 and yesterday 00:00
- if (i > 0) {
- itemList.add(new SectionItem(context.getResources().getString(foundation.e.notes.R.string.listview_updated_yesterday)));
- }
- yesterdaySet = true;
- } else if (!weekSet && currentNote.getModified().getTimeInMillis() < yesterday.getTimeInMillis() && currentNote.getModified().getTimeInMillis() >= week.getTimeInMillis()) {
- // between yesterday 00:00 and start of the week 00:00
- if (i > 0) {
- itemList.add(new SectionItem(context.getResources().getString(foundation.e.notes.R.string.listview_updated_this_week)));
- }
- weekSet = true;
- } else if (!monthSet && currentNote.getModified().getTimeInMillis() < week.getTimeInMillis() && currentNote.getModified().getTimeInMillis() >= month.getTimeInMillis()) {
- // between start of the week 00:00 and start of the month 00:00
- if (i > 0) {
- itemList.add(new SectionItem(context.getResources().getString(foundation.e.notes.R.string.listview_updated_this_month)));
- }
- monthSet = true;
- } else if (!earlierSet && currentNote.getModified().getTimeInMillis() < month.getTimeInMillis()) {
- // before start of the month 00:00
- if (i > 0) {
- itemList.add(new SectionItem(context.getResources().getString(foundation.e.notes.R.string.listview_updated_earlier)));
- }
- earlierSet = true;
+ String timeslot = timeslotter.getTimeslot(currentNote);
+ if(i > 0 && !timeslot.equals(lastTimeslot)) {
+ itemList.add(new SectionItem(timeslot));
}
itemList.add(colorTheNote(currentNote));
+ lastTimeslot = timeslot;
}
return itemList;
@@ -182,4 +119,53 @@ public class LoadNotesListTask extends AsyncTask> {
public interface NotesLoadedListener {
void onNotesLoaded(List
- notes, boolean showCategory);
}
+
+ private class Timeslotter {
+ private final List timeslots = new ArrayList<>();
+ private final Calendar lastYear;
+
+ Timeslotter() {
+ Calendar now = Calendar.getInstance();
+ int month = now.get(Calendar.MONTH);
+ int day = now.get(Calendar.DAY_OF_MONTH);
+ int offsetWeekStart = (now.get(Calendar.DAY_OF_WEEK) - now.getFirstDayOfWeek() + 7) % 7;
+ timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_today), month, day));
+ timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_yesterday), month,day - 1));
+ timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_this_week), month,day - offsetWeekStart));
+ timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_last_week), month,day - offsetWeekStart - 7));
+ timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_this_month), month,1));
+ timeslots.add(new Timeslot(context.getResources().getString(R.string.listview_updated_last_month), month - 1, 1));
+ lastYear = Calendar.getInstance();
+ lastYear.set(now.get(Calendar.YEAR) - 1, 0, 1, 0, 0, 0);
+ }
+
+ String getTimeslot(DBNote note) {
+ if (note.isFavorite()) {
+ return "";
+ }
+ Calendar modified = note.getModified();
+ for (Timeslot timeslot : timeslots) {
+ if (!modified.before(timeslot.time)) {
+ return timeslot.label;
+ }
+ }
+ if (!modified.before(this.lastYear)) {
+ // use YEAR and MONTH in a format based on current locale
+ return DateUtils.formatDateTime(context, modified.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_NO_MONTH_DAY);
+ } else {
+ return Integer.toString(modified.get(Calendar.YEAR));
+ }
+ }
+
+ private class Timeslot {
+ final String label;
+ final Calendar time;
+
+ Timeslot(String label, int month, int day) {
+ this.label = label;
+ this.time = Calendar.getInstance();
+ this.time.set(this.time.get(Calendar.YEAR), month, day, 0, 0, 0);
+ }
+ }
+ }
}
diff --git a/app/src/main/java/foundation/e/notes/persistence/NoteSQLiteOpenHelper.java b/app/src/main/java/foundation/e/notes/persistence/NoteSQLiteOpenHelper.java
index e04382eb331c2438d1d1050ecd5f9e2dffff84dc..210705189db47a9fe75eea31b767a7f96532c813 100644
--- a/app/src/main/java/foundation/e/notes/persistence/NoteSQLiteOpenHelper.java
+++ b/app/src/main/java/foundation/e/notes/persistence/NoteSQLiteOpenHelper.java
@@ -3,17 +3,21 @@ package foundation.e.notes.persistence;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -26,12 +30,7 @@ import foundation.e.notes.util.ICallback;
import foundation.e.notes.util.NoteUtil;
import foundation.e.notes.android.appwidget.NoteListWidget;
import foundation.e.notes.android.appwidget.SingleNoteWidget;
-import foundation.e.notes.model.CloudNote;
-import foundation.e.notes.model.DBNote;
-import foundation.e.notes.model.DBStatus;
-import foundation.e.notes.model.NavigationAdapter;
-import foundation.e.notes.util.ICallback;
-import foundation.e.notes.util.NoteUtil;
+import foundation.e.notes.R;
/**
* Helps to add, get, update and delete Notes with the option to trigger a Resync with the Server.
@@ -55,14 +54,13 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper {
private static NoteSQLiteOpenHelper instance;
- private NoteServerSyncHelper serverSyncHelper = null;
- private Context context = null;
+ private NoteServerSyncHelper serverSyncHelper;
+ private Context context;
private NoteSQLiteOpenHelper(Context context) {
super(context, database_name, null, database_version);
this.context = context.getApplicationContext();
serverSyncHelper = NoteServerSyncHelper.getInstance(this);
- //recreateDatabase(getWritableDatabase());
}
public static NoteSQLiteOpenHelper getInstance(Context context) {
@@ -250,11 +248,17 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper {
@NonNull
@WorkerThread
private List getNotesCustom(@NonNull String selection, @NonNull String[] selectionArgs, @Nullable String orderBy) {
+ return this.getNotesCustom(selection, selectionArgs, orderBy, null);
+ }
+
+ @NonNull
+ @WorkerThread
+ private List getNotesCustom(@NonNull String selection, @NonNull String[] selectionArgs, @Nullable String orderBy, @Nullable String limit) {
SQLiteDatabase db = getReadableDatabase();
if (selectionArgs.length > 2) {
Log.v("Note", selection + " ---- " + selectionArgs[0] + " " + selectionArgs[1] + " " + selectionArgs[2]);
}
- Cursor cursor = db.query(table_notes, columns, selection, selectionArgs, null, null, orderBy);
+ Cursor cursor = db.query(table_notes, columns, selection, selectionArgs, null, null, orderBy, limit);
List notes = new ArrayList<>();
while (cursor.moveToNext()) {
notes.add(getNoteFromCursor(cursor));
@@ -308,6 +312,12 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper {
return getNotesCustom(key_status + " != ?", new String[]{DBStatus.LOCAL_DELETED.getTitle()}, default_order);
}
+ @NonNull
+ @WorkerThread
+ public List getRecentNotes() {
+ return getNotesCustom(key_status + " != ?", new String[]{DBStatus.LOCAL_DELETED.getTitle()}, key_modified + " DESC", "4");
+ }
+
/**
* Returns a list of all Notes in the Database
*
@@ -333,7 +343,7 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper {
}
if (category != null) {
- where.add(key_category + "=? OR " + key_category + " LIKE ?");
+ where.add("(" + key_category + "=? OR " + key_category + " LIKE ? )");
args.add(category);
args.add(category + "/%");
}
@@ -533,6 +543,17 @@ public class NoteSQLiteOpenHelper extends SQLiteOpenHelper {
new String[]{String.valueOf(id)});
notifyNotesChanged();
getNoteServerSyncHelper().scheduleSync(true);
+
+ if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
+ shortcutManager.getPinnedShortcuts().forEach((shortcut) -> {
+ String shortcutId = id + "";
+ if (shortcut.getId().equals(shortcutId)) {
+ Log.v(NoteSQLiteOpenHelper.class.getSimpleName(), "Removing shortcut for " + shortcutId);
+ shortcutManager.disableShortcuts(Collections.singletonList(shortcutId), context.getResources().getString(R.string.note_has_been_deleted));
+ }
+ });
+ }
return i;
}
diff --git a/app/src/main/java/foundation/e/notes/persistence/NoteServerSyncHelper.java b/app/src/main/java/foundation/e/notes/persistence/NoteServerSyncHelper.java
index 8cbab6c115f8e81a435964806ed3df805a44f2d3..e66f91a3ffa2a58069c46d3614134b744b42d5e7 100644
--- a/app/src/main/java/foundation/e/notes/persistence/NoteServerSyncHelper.java
+++ b/app/src/main/java/foundation/e/notes/persistence/NoteServerSyncHelper.java
@@ -13,6 +13,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.IBinder;
+import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
@@ -29,6 +30,8 @@ import java.util.Set;
import foundation.e.cert4android.CustomCertManager;
import foundation.e.cert4android.CustomCertService;
+import foundation.e.cert4android.ICustomCertService;
+import foundation.e.cert4android.IOnCertificateDecision;
import foundation.e.notes.android.activity.SettingsActivity;
import foundation.e.notes.model.CloudNote;
import foundation.e.notes.model.DBNote;
@@ -39,15 +42,6 @@ import foundation.e.notes.util.NotesClientUtil;
import foundation.e.notes.util.ServerResponse;
import foundation.e.notes.util.SupportUtil;
import foundation.e.notes.R;
-import foundation.e.notes.android.activity.SettingsActivity;
-import foundation.e.notes.model.CloudNote;
-import foundation.e.notes.model.DBNote;
-import foundation.e.notes.model.DBStatus;
-import foundation.e.notes.util.ICallback;
-import foundation.e.notes.util.NotesClient;
-import foundation.e.notes.util.NotesClientUtil.LoginStatus;
-import foundation.e.notes.util.ServerResponse;
-import foundation.e.notes.util.SupportUtil;
/**
* @author Nihar Thakkar
@@ -81,9 +75,23 @@ public class NoteServerSyncHelper {
private final Context appContext;
private CustomCertManager customCertManager;
+ private ICustomCertService iCustomCertService;
// Track network connection changes using a BroadcastReceiver
private boolean networkConnected = false;
+ private String syncOnlyOnWifiKey;
+ private boolean syncOnlyOnWifi;
+
+ /**
+ * @see Do not make this a local variable.
+ */
+ private SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = (SharedPreferences prefs, String key) -> {
+ if (syncOnlyOnWifiKey.equals(key)) {
+ syncOnlyOnWifi = prefs.getBoolean(syncOnlyOnWifiKey, false);
+ updateNetworkStatus();
+ }
+ };
+
private final BroadcastReceiver networkReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -98,6 +106,7 @@ public class NoteServerSyncHelper {
private final ServiceConnection certService = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ iCustomCertService = ICustomCertService.Stub.asInterface(iBinder);
cert4androidReady = true;
if (isSyncPossible()) {
scheduleSync(false);
@@ -107,6 +116,7 @@ public class NoteServerSyncHelper {
@Override
public void onServiceDisconnected(ComponentName componentName) {
cert4androidReady = false;
+ iCustomCertService = null;
}
};
@@ -122,6 +132,7 @@ public class NoteServerSyncHelper {
private NoteServerSyncHelper(NoteSQLiteOpenHelper db) {
this.dbHelper = db;
this.appContext = db.getContext().getApplicationContext();
+ this.syncOnlyOnWifiKey = appContext.getResources().getString(R.string.pref_key_wifi_only);
new Thread() {
@Override
public void run() {
@@ -131,6 +142,11 @@ public class NoteServerSyncHelper {
// Registers BroadcastReceiver to track network connection changes.
appContext.registerReceiver(networkReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.appContext);
+ prefs.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
+ syncOnlyOnWifi = prefs.getBoolean(syncOnlyOnWifiKey, false);
+
updateNetworkStatus();
// bind to certifciate service to block sync attempts if service is not ready
appContext.bindService(new Intent(appContext, CustomCertService.class), certService, Context.BIND_AUTO_CREATE);
@@ -202,6 +218,10 @@ public class NoteServerSyncHelper {
return customCertManager;
}
+ public void checkCertificate(byte[] cert, boolean foreground, IOnCertificateDecision callback) throws RemoteException {
+ iCustomCertService.checkTrusted(cert, true, foreground, callback);
+ }
+
/**
* Adds a callback method to the NoteServerSyncHelper for the synchronization part push local changes to the server.
* All callbacks will be executed once the synchronization operations are done.
@@ -265,9 +285,18 @@ public class NoteServerSyncHelper {
private void updateNetworkStatus() {
ConnectivityManager connMgr = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
+
if (activeInfo != null && activeInfo.isConnected()) {
- Log.d(NoteServerSyncHelper.class.getSimpleName(), "Network connection established.");
- networkConnected = true;
+ networkConnected =
+ !syncOnlyOnWifi || ((ConnectivityManager) appContext
+ .getSystemService(Context.CONNECTIVITY_SERVICE))
+ .getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
+
+ if (networkConnected) {
+ Log.d(NoteServerSyncHelper.class.getSimpleName(), "Network connection established.");
+ } else {
+ Log.d(NoteServerSyncHelper.class.getSimpleName(), "Network connected, but not used because only synced on wifi.");
+ }
} else {
networkConnected = false;
Log.d(NoteServerSyncHelper.class.getSimpleName(), "No network connection.");
diff --git a/app/src/main/java/foundation/e/notes/util/DisplayUtils.java b/app/src/main/java/foundation/e/notes/util/DisplayUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..7efc13e22b50aa51b44bc30a3469bbea7920b45a
--- /dev/null
+++ b/app/src/main/java/foundation/e/notes/util/DisplayUtils.java
@@ -0,0 +1,62 @@
+/*
+ * Nextcloud Notes application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2018 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package foundation.e.notes.util;
+
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import androidx.annotation.ColorInt;
+
+public class DisplayUtils {
+
+ public static Spannable searchAndColor(String text, Spannable spannable, String searchText, @ColorInt int color) {
+
+ Object spansToRemove[] = spannable.getSpans(0, text.length(), Object.class);
+ for(Object span: spansToRemove){
+ if(span instanceof CharacterStyle)
+ spannable.removeSpan(span);
+ }
+
+ if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchText)) {
+ return spannable;
+ }
+
+ Matcher m = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE | Pattern.LITERAL)
+ .matcher(text);
+
+
+ while (m.find()) {
+ int start = m.start();
+ int end = m.end();
+ spannable.setSpan(new ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ return spannable;
+ }
+
+}
diff --git a/app/src/main/java/foundation/e/notes/util/ExceptionHandler.java b/app/src/main/java/foundation/e/notes/util/ExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..830dc7904387037deaca658dcd5de3b5b465db75
--- /dev/null
+++ b/app/src/main/java/foundation/e/notes/util/ExceptionHandler.java
@@ -0,0 +1,35 @@
+package foundation.e.notes.util;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.io.Serializable;
+
+import foundation.e.notes.android.activity.ExceptionActivity;
+
+import static foundation.e.notes.android.activity.ExceptionActivity.KEY_THROWABLE;
+
+public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
+
+ private Activity context;
+
+ public ExceptionHandler(Activity context) {
+ super();
+ this.context = context;
+ }
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ e.printStackTrace();
+ Intent intent = new Intent(context.getApplicationContext(), ExceptionActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ Bundle extras = new Bundle();
+ intent.putExtra(KEY_THROWABLE, (Serializable) e);
+ extras.putSerializable(KEY_THROWABLE, e);
+ intent.putExtras(extras);
+ context.getApplicationContext().startActivity(intent);
+ context.finish();
+ Runtime.getRuntime().exit(0);
+ }
+}
diff --git a/app/src/main/java/foundation/e/notes/util/MarkDownUtil.java b/app/src/main/java/foundation/e/notes/util/MarkDownUtil.java
index 4b748efc50606abf1aee104ead8c319478808503..1a9970c408133dd10df833b45516dc0788bb6554 100644
--- a/app/src/main/java/foundation/e/notes/util/MarkDownUtil.java
+++ b/app/src/main/java/foundation/e/notes/util/MarkDownUtil.java
@@ -1,7 +1,9 @@
package foundation.e.notes.util;
import android.content.Context;
-import android.support.v4.content.res.ResourcesCompat;
+
+import androidx.core.content.ContextCompat;
+import androidx.core.content.res.ResourcesCompat;
import com.yydcdut.rxmarkdown.RxMDConfiguration;
import com.yydcdut.rxmarkdown.RxMDConfiguration.Builder;
@@ -22,7 +24,21 @@ public class MarkDownUtil {
*/
public static Builder getMarkDownConfiguration(Context context) {
return new RxMDConfiguration.Builder(context)
- .setUnOrderListColor(ResourcesCompat.getColor(context.getResources(), R.color.fg_default, null))
+ .setUnOrderListColor(ContextCompat.getColor(context, R.color.fg_default))
+ .setCodeBgColor(ContextCompat.getColor(context, R.color.fg_default_high))
+ .setHeader2RelativeSize(1.35f)
+ .setHeader3RelativeSize(1.25f)
+ .setHeader4RelativeSize(1.15f)
+ .setHeader5RelativeSize(1.1f)
+ .setHeader6RelativeSize(1.05f)
+ .setHorizontalRulesHeight(2)
+ .setLinkFontColor(ContextCompat.getColor(context, R.color.primary));
+ }
+
+ public static Builder getMarkDownConfiguration(Context context, Boolean darkTheme) {
+ return new RxMDConfiguration.Builder(context)
+ .setUnOrderListColor(ResourcesCompat.getColor(context.getResources(),
+ darkTheme ? R.color.widget_fg_dark_theme : R.color.widget_fg_default, null))
.setHeader2RelativeSize(1.35f)
.setHeader3RelativeSize(1.25f)
.setHeader4RelativeSize(1.15f)
diff --git a/app/src/main/java/foundation/e/notes/util/NoteUtil.java b/app/src/main/java/foundation/e/notes/util/NoteUtil.java
index 6f49b8450aeb454c58bc504cf12b6afedbc775c5..a4537f0fd2846ecfd81114a57a7a08adaf90e627 100644
--- a/app/src/main/java/foundation/e/notes/util/NoteUtil.java
+++ b/app/src/main/java/foundation/e/notes/util/NoteUtil.java
@@ -1,8 +1,8 @@
package foundation.e.notes.util;
import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.util.regex.Pattern;
diff --git a/app/src/main/java/foundation/e/notes/util/Notes.java b/app/src/main/java/foundation/e/notes/util/Notes.java
index 8e4616e38c49e9604fcffc371246d262af1c26c3..4be2541529198fa7d96c0ed871609b978bf4ac3d 100644
--- a/app/src/main/java/foundation/e/notes/util/Notes.java
+++ b/app/src/main/java/foundation/e/notes/util/Notes.java
@@ -4,7 +4,7 @@ import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.support.v7.app.AppCompatDelegate;
+import androidx.appcompat.app.AppCompatDelegate;
public class Notes extends Application {
private static final String DARK_THEME = "darkTheme";
diff --git a/app/src/main/java/foundation/e/notes/util/NotesClient.java b/app/src/main/java/foundation/e/notes/util/NotesClient.java
index 231f501627957cb4706a28212fb071afce083a07..a588c842883843a701ea3d7c5ca83f751b59688e 100644
--- a/app/src/main/java/foundation/e/notes/util/NotesClient.java
+++ b/app/src/main/java/foundation/e/notes/util/NotesClient.java
@@ -1,6 +1,6 @@
package foundation.e.notes.util;
-import android.support.annotation.WorkerThread;
+import androidx.annotation.WorkerThread;
import android.util.Base64;
import android.util.Log;
diff --git a/app/src/main/java/foundation/e/notes/util/NotesClientUtil.java b/app/src/main/java/foundation/e/notes/util/NotesClientUtil.java
index 45290b067a6c512815f8f8b46a8046a67cdb58de..e57ed810406ee6c1a0880473e531be7c94a85daf 100644
--- a/app/src/main/java/foundation/e/notes/util/NotesClientUtil.java
+++ b/app/src/main/java/foundation/e/notes/util/NotesClientUtil.java
@@ -1,6 +1,6 @@
package foundation.e.notes.util;
-import android.support.annotation.StringRes;
+import androidx.annotation.StringRes;
import android.util.Base64;
import android.util.Log;
diff --git a/app/src/main/java/foundation/e/notes/util/StyleCallback.java b/app/src/main/java/foundation/e/notes/util/StyleCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3d5d06d64d2575fe8f518fe0ef91c13979f5518
--- /dev/null
+++ b/app/src/main/java/foundation/e/notes/util/StyleCallback.java
@@ -0,0 +1,130 @@
+package foundation.e.notes.util;
+
+import android.graphics.Typeface;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
+import android.util.SparseIntArray;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.EditText;
+
+import foundation.e.notes.R;
+
+public class StyleCallback implements ActionMode.Callback {
+
+ private EditText editText;
+
+ public StyleCallback(EditText editText) {
+ this.editText = editText;
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ MenuInflater inflater = mode.getMenuInflater();
+ inflater.inflate(R.menu.style, menu);
+ menu.removeItem(android.R.id.selectAll);
+
+ SparseIntArray styleFormatMap = new SparseIntArray();
+ styleFormatMap.append(R.id.bold, Typeface.BOLD);
+ styleFormatMap.append(R.id.italic, Typeface.ITALIC);
+
+ MenuItem item;
+ CharSequence title;
+ SpannableStringBuilder ssb;
+
+ for (int i = 0; i < styleFormatMap.size(); i++) {
+ item = menu.findItem(styleFormatMap.keyAt(i));
+ title = item.getTitle();
+ ssb = new SpannableStringBuilder(title);
+ ssb.setSpan(new StyleSpan(styleFormatMap.valueAt(i)), 0, title.length(), 0);
+ item.setTitle(ssb);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ int start = editText.getSelectionStart();
+ int end = editText.getSelectionEnd();
+ SpannableStringBuilder ssb = new SpannableStringBuilder(editText.getText());
+ final String markdown;
+
+
+ switch (item.getItemId()) {
+ case R.id.bold:
+ markdown = "**";
+ if (hasAlreadyMarkdown(start, end, markdown)) {
+ this.removeMarkdown(ssb, start, end, markdown);
+ } else {
+ this.addMarkdown(ssb, start, end, markdown, Typeface.BOLD);
+ }
+ editText.setText(ssb);
+ editText.setSelection(end + markdown.length() * 2);
+ break;
+ case R.id.italic:
+ markdown = "*";
+ if (hasAlreadyMarkdown(start, end, markdown)) {
+ this.removeMarkdown(ssb, start, end, markdown);
+ } else {
+ this.addMarkdown(ssb, start, end, markdown, Typeface.ITALIC);
+ }
+ editText.setText(ssb);
+ editText.setSelection(end + markdown.length() * 2);
+ break;
+ case R.id.link:
+ boolean textToFormatIsLink = TextUtils.indexOf(editText.getText().subSequence(start, end), "http") == 0;
+ if(textToFormatIsLink) {
+ ssb.insert(end, ")");
+ ssb.insert(start, "[](");
+ } else {
+ ssb.insert(end, "]()");
+ ssb.insert(start, "[");
+ }
+ end++;
+ ssb.setSpan(new StyleSpan(Typeface.NORMAL), start, end, 1);
+ editText.setText(ssb);
+ if(textToFormatIsLink) {
+ editText.setSelection(start + 1);
+ } else {
+ editText.setSelection(end + 2); // after ](
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+
+ }
+
+ private boolean hasAlreadyMarkdown(int start, int end, String markdown) {
+ return start > markdown.length() && markdown.contentEquals(editText.getText().subSequence(start - markdown.length(), start)) &&
+ editText.getText().length() > end + markdown.length() && markdown.contentEquals(editText.getText().subSequence(end, end + markdown.length()));
+ }
+
+ private void removeMarkdown(SpannableStringBuilder ssb, int start, int end, String markdown) {
+ // FIXME disabled, because it does not work properly and might cause data loss
+ // ssb.delete(start - markdown.length(), start);
+ // ssb.delete(end - markdown.length(), end);
+ // ssb.setSpan(new StyleSpan(Typeface.NORMAL), start, end, 1);
+ }
+
+ private void addMarkdown(SpannableStringBuilder ssb, int start, int end, String markdown, int typeface) {
+ ssb.insert(end, markdown);
+ ssb.insert(start, markdown);
+ editText.getText().charAt(start);
+ editText.getText().charAt(start + 1);
+ end += markdown.length() * 2;
+ ssb.setSpan(new StyleSpan(typeface), start, end, 1);
+ }
+}
diff --git a/app/src/main/java/foundation/e/notes/util/SupportUtil.java b/app/src/main/java/foundation/e/notes/util/SupportUtil.java
index 3c8f77ff38e752b338aacc6389b58b3f596632d7..b8a77976d437ad6efcb5d5e989bcbadcf0a7d0ee 100644
--- a/app/src/main/java/foundation/e/notes/util/SupportUtil.java
+++ b/app/src/main/java/foundation/e/notes/util/SupportUtil.java
@@ -4,13 +4,14 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
-import android.support.annotation.WorkerThread;
import android.text.Html;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.widget.TextView;
+import androidx.annotation.WorkerThread;
+
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
@@ -93,6 +94,6 @@ public class SupportUtil {
@WorkerThread
public static CustomCertManager getCertManager(Context ctx) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
- return new CustomCertManager(ctx, preferences.getBoolean(ctx.getString(R.string.pref_key_trust_system_certs), true));
+ return new CustomCertManager(ctx, preferences.getBoolean(ctx.getString(R.string.pref_key_trust_system_certs), true), true, true);
}
}
diff --git a/app/src/main/res/drawable/background.png b/app/src/main/res/drawable/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..90856f4c889605689ea8658893fb0d4a7c1770bc
Binary files /dev/null and b/app/src/main/res/drawable/background.png differ
diff --git a/app/src/main/res/drawable/ic_format_bold_black_24dp.xml b/app/src/main/res/drawable/ic_format_bold_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..625077f905b7dbe0720fb0fdecf8e57502e4c756
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_bold_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_format_italic_black_24dp.xml b/app/src/main/res/drawable/ic_format_italic_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..48f2605e5aba1a022a3a6b3126b53f05481566cf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_italic_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_format_size_black_24dp.xml b/app/src/main/res/drawable/ic_format_size_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cc8872c1738c530734d3217dba2d5a361c7cf722
--- /dev/null
+++ b/app/src/main/res/drawable/ic_format_size_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_insert_link_black_24dp.xml b/app/src/main/res/drawable/ic_insert_link_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3672c276598e854fd89ecc514ed2d4d2822846dc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_insert_link_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_library_music_black_24dp.xml b/app/src/main/res/drawable/ic_library_music_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e90b1c40b790470200052c7920108350a1309214
--- /dev/null
+++ b/app/src/main/res/drawable/ic_library_music_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_sync_black_24dp.xml b/app/src/main/res/drawable/ic_sync_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3628dedbfc2351b625557570d7d96239b8694abc
--- /dev/null
+++ b/app/src/main/res/drawable/ic_sync_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_text_fields_black_24dp.xml b/app/src/main/res/drawable/ic_text_fields_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b9117ad996af59ad71792c44e6b2f164d87c6c54
--- /dev/null
+++ b/app/src/main/res/drawable/ic_text_fields_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_wifi_black_24dp.xml b/app/src/main/res/drawable/ic_wifi_black_24dp.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d579899132085a383740bb854d8cf52a0189b63b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wifi_black_24dp.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
index 00f694effb418b7c75dbfd3b639b7dc6952a8912..78e17475d72b0ceb571b35177bfb6c7827b4f52b 100644
--- a/app/src/main/res/layout/activity_about.xml
+++ b/app/src/main/res/layout/activity_about.xml
@@ -4,12 +4,12 @@
android:layout_height="match_parent"
android:orientation="vertical">
-
-
diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml
index 855689504ee3612d267ab70f27bd90af3f9a91aa..2978994e4b288d51bbb3fc9778a67b152aa44638 100644
--- a/app/src/main/res/layout/activity_account.xml
+++ b/app/src/main/res/layout/activity_account.xml
@@ -1,5 +1,5 @@
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_exception.xml b/app/src/main/res/layout/activity_exception.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d512dfa7935672e9435c61600120416379c7ca46
--- /dev/null
+++ b/app/src/main/res/layout/activity_exception.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_note_list_configuration.xml b/app/src/main/res/layout/activity_note_list_configuration.xml
index 019b72247a327574bbd3ec690b3d215b07f03b95..209ab66ad91b2de1636c2c2dac72f1e1f30228c2 100644
--- a/app/src/main/res/layout/activity_note_list_configuration.xml
+++ b/app/src/main/res/layout/activity_note_list_configuration.xml
@@ -4,10 +4,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
-
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
diff --git a/app/src/main/res/layout/activity_notes_list_view.xml b/app/src/main/res/layout/activity_notes_list_view.xml
index d562c8c369b2e466b81c270aa57fe6a9a68d6e5e..e39fed996c86e5d2e83fbf2dba70a5e731bd297a 100644
--- a/app/src/main/res/layout/activity_notes_list_view.xml
+++ b/app/src/main/res/layout/activity_notes_list_view.xml
@@ -1,5 +1,5 @@
-
-
-
-
-
+
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index ee39da1c49dc41b1c3ce6247dd1f17e51b8a8e84..3ebe9396ae713209c41766d6ff380399852eb265 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -8,7 +8,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin">
-
-
+
-
+ android:layout_height="wrap_content"
+ android:visibility="gone">
-
+
-
+ app:passwordToggleEnabled="true"
+ android:visibility="gone">
-
+