diff --git a/README.md b/README.md index d15aeb14..0dc125e6 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,15 @@ An android client for [Nextcloud Notes App](https://github.com/nextcloud/notes/) ## :arrow_forward: Access -[![Download from Google Play](https://play.google.com/intl/en_us/badges/images/badge_new.png)](https://play.google.com/store/apps/details?id=it.niedermann.owncloud.notes) -[![Nextcloud Notes App on fdroid.org](https://camo.githubusercontent.com/7df0eafa4433fa4919a56f87c3d99cf81b68d01c/68747470733a2f2f662d64726f69642e6f72672f77696b692f696d616765732f632f63342f462d44726f69642d627574746f6e5f617661696c61626c652d6f6e2e706e67)](https://f-droid.org/repository/browse/?fdid=it.niedermann.owncloud.notes) -[![Donate with PayPal](https://raw.githubusercontent.com/stefan-niedermann/paypal-donate-button/master/paypal-donate-button.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K7HVLE6J7SXXA) +[Get it on Play Store](https://play.google.com/store/apps/details?id=it.niedermann.owncloud.notes) +[Get it on F-Droid](https://f-droid.org/repository/browse/?fdid=it.niedermann.owncloud.notes) +[Donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K7HVLE6J7SXXA) [![Donate using Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/stefan-niedermann/donate) ## :eyes: Screenshots diff --git a/app/build.gradle b/app/build.gradle index e6ec18db..449b2700 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 28 - buildToolsVersion '28.0.3' + buildToolsVersion '29.0.1' compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -13,8 +13,8 @@ android { applicationId "it.niedermann.owncloud.notes" minSdkVersion 14 targetSdkVersion 28 - versionCode 40 - versionName "0.24.0" + versionCode 47 + versionName "0.27.1" } buildTypes { release { @@ -36,10 +36,10 @@ dependencies { implementation 'com.yydcdut:markdown-processor:0.1.3' implementation 'com.yydcdut:rxmarkdown-wrapper:0.1.3' - implementation 'com.jakewharton:butterknife:10.0.0' - annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0' + implementation 'com.jakewharton:butterknife:10.2.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0' - implementation "androidx.appcompat:appcompat:1.0.2" + implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.recyclerview:recyclerview:1.0.0" implementation "com.google.android.material:material:1.0.0" diff --git a/app/release/app-release.apk b/app/release/app-release.apk deleted file mode 100644 index 6540c6a2..00000000 Binary files a/app/release/app-release.apk and /dev/null differ diff --git a/app/release/output.json b/app/release/output.json deleted file mode 100644 index 63754a0b..00000000 --- a/app/release/output.json +++ /dev/null @@ -1 +0,0 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":37,"versionName":"0.22.3","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 58eddca6..3af99f24 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,9 +10,9 @@ android:allowBackup="true" android:fullBackupContent="true" android:icon="@mipmap/ic_launcher" - android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" + android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> @@ -98,6 +98,10 @@ + + diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java index 937d2a20..0debdf5c 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/AboutActivity.java @@ -1,12 +1,14 @@ package it.niedermann.owncloud.notes.android.activity; import android.os.Bundle; -import com.google.android.material.tabs.TabLayout; + +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 androidx.appcompat.app.AppCompatActivity; + +import com.google.android.material.tabs.TabLayout; import butterknife.BindView; import butterknife.ButterKnife; @@ -14,6 +16,7 @@ import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.fragment.about.AboutFragmentContributingTab; import it.niedermann.owncloud.notes.android.fragment.about.AboutFragmentCreditsTab; import it.niedermann.owncloud.notes.android.fragment.about.AboutFragmentLicenseTab; +import it.niedermann.owncloud.notes.util.ExceptionHandler; public class AboutActivity extends AppCompatActivity { @@ -25,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); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java index 20b4b254..4c205d15 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/EditNoteActivity.java @@ -9,10 +9,11 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import java.util.Calendar; - import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; + +import java.util.Calendar; + import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.fragment.BaseNoteFragment; import it.niedermann.owncloud.notes.android.fragment.NoteEditFragment; @@ -20,10 +21,12 @@ import it.niedermann.owncloud.notes.android.fragment.NotePreviewFragment; import it.niedermann.owncloud.notes.model.Category; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; +import it.niedermann.owncloud.notes.util.ExceptionHandler; import it.niedermann.owncloud.notes.util.NoteUtil; 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"; @@ -34,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(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/ExceptionActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/ExceptionActivity.java new file mode 100644 index 00000000..1f1c862a --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/ExceptionActivity.java @@ -0,0 +1,63 @@ +package it.niedermann.owncloud.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 it.niedermann.owncloud.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(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/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java index 14e9e213..65280d01 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/NotesListViewActivity.java @@ -3,27 +3,16 @@ package it.niedermann.owncloud.notes.android.activity; 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 androidx.annotation.Nullable; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.snackbar.Snackbar; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.view.ActionMode; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.SearchView; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -33,6 +22,23 @@ import android.widget.LinearLayout; 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.appcompat.view.ActionMode; +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.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -48,16 +54,22 @@ import it.niedermann.owncloud.notes.model.NavigationAdapter; import it.niedermann.owncloud.notes.persistence.LoadNotesListTask; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper; +import it.niedermann.owncloud.notes.util.ExceptionHandler; import it.niedermann.owncloud.notes.util.ICallback; import it.niedermann.owncloud.notes.util.NoteUtil; import it.niedermann.owncloud.notes.util.NotesClientUtil; +import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT; + public class NotesListViewActivity extends AppCompatActivity implements ItemAdapter.NoteClickListener { - public final static String CREATED_NOTE = "it.niedermann.owncloud.notes.created_notes"; - public final static String CREDENTIALS_CHANGED = "it.niedermann.owncloud.notes.CREDENTIALS_CHANGED"; + public static final String CREATED_NOTE = "it.niedermann.owncloud.notes.created_notes"; + public static final String CREDENTIALS_CHANGED = "it.niedermann.owncloud.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 = "it.niedermann.owncloud.notes.favorites"; + public static final String ACTION_RECENT = "it.niedermann.owncloud.notes.recent"; + private static final String SAVED_STATE_NAVIGATION_SELECTION = "navigationSelection"; private static final String SAVED_STATE_NAVIGATION_ADAPTER_SLECTION = "navigationAdapterSelection"; @@ -104,6 +116,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 @@ -114,13 +149,21 @@ 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, SettingsActivity.class); startActivityForResult(settingsIntent, server_settings); } 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); @@ -178,26 +221,20 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap private void setupNotesList() { initList(); // Pull to Refresh - swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { - @Override - public void onRefresh() { - if (db.getNoteServerSyncHelper().isSyncPossible()) { - synchronize(); - } else { - swipeRefreshLayout.setRefreshing(false); - Toast.makeText(getApplicationContext(), getString(R.string.error_sync, getString(NotesClientUtil.LoginStatus.NO_NETWORK.str)), Toast.LENGTH_LONG).show(); - } + swipeRefreshLayout.setOnRefreshListener(() -> { + if (db.getNoteServerSyncHelper().isSyncPossible()) { + synchronize(); + } else { + swipeRefreshLayout.setRefreshing(false); + Toast.makeText(getApplicationContext(), getString(R.string.error_sync, getString(NotesClientUtil.LoginStatus.NO_NETWORK.str)), Toast.LENGTH_LONG).show(); } }); // 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); }); } @@ -371,12 +408,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.account.setOnClickListener((View v) -> { + Intent settingsIntent = new Intent(that, SettingsActivity.class); + startActivityForResult(settingsIntent, server_settings); }); adapterMenu.setItems(itemsMenu); @@ -389,7 +423,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; } @@ -401,7 +435,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); } @@ -413,8 +447,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()); @@ -422,14 +456,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; @@ -444,16 +475,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); } }); @@ -463,6 +494,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) { @@ -482,14 +514,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); @@ -522,6 +551,7 @@ public class NotesListViewActivity extends AppCompatActivity implements ItemAdap searchEditFrame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { int oldVisibility = -1; + @Override public void onGlobalLayout() { int currentVisibility = searchEditFrame.getVisibility(); @@ -530,11 +560,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); } @@ -582,28 +609,36 @@ 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 == server_settings) { - // Create new Instance with new URL and credentials - db = NoteSQLiteOpenHelper.getInstance(this); - if (db.getNoteServerSyncHelper().isSyncPossible()) { - this.updateUsernameInDrawer(); - adapter.removeAll(); - synchronize(); - } else { - Toast.makeText(getApplicationContext(), getString(R.string.error_sync, getString(NotesClientUtil.LoginStatus.NO_NETWORK.str)), Toast.LENGTH_LONG).show(); - } + // Recreate activity completely, because theme switchting makes problems when only invalidating the views. + // @see https://github.com/stefan-niedermann/nextcloud-notes/issues/529 + recreate(); } } 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)) { + String url = preferences.getString(SettingsActivity.SETTINGS_URL, SettingsActivity.DEFAULT_SETTINGS); + if (url != null) { + url = url.replace("https://", "").replace("http://", ""); + } else { + Log.w(NotesListViewActivity.class.getSimpleName(), "url is null"); + } + if (!SettingsActivity.DEFAULT_SETTINGS.equals(username) && !SettingsActivity.DEFAULT_SETTINGS.equals(url)) { this.account.setText(username + "@" + url.substring(0, url.length() - 1)); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java index 2d64d596..849a93ff 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/PreferencesActivity.java @@ -1,10 +1,12 @@ package it.niedermann.owncloud.notes.android.activity; import android.os.Bundle; + import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import it.niedermann.owncloud.notes.android.fragment.PreferencesFragment; +import it.niedermann.owncloud.notes.util.ExceptionHandler; /** * Allows to change application settings. @@ -14,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/it/niedermann/owncloud/notes/android/activity/SelectSingleNoteActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SelectSingleNoteActivity.java index 2e1e1d86..2e89eb35 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SelectSingleNoteActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/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 androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import android.view.Menu; import android.view.View; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import butterknife.BindView; import butterknife.ButterKnife; import it.niedermann.owncloud.notes.R; @@ -17,6 +18,7 @@ import it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.model.Item; import it.niedermann.owncloud.notes.model.ItemAdapter; +import it.niedermann.owncloud.notes.util.ExceptionHandler; import it.niedermann.owncloud.notes.util.Notes; 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(); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SettingsActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SettingsActivity.java index ea9d1777..8c5caf05 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SettingsActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SettingsActivity.java @@ -6,9 +6,6 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; -import com.google.android.material.textfield.TextInputLayout; -import androidx.core.content.ContextCompat; -import androidx.appcompat.app.AppCompatActivity; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -19,12 +16,18 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; + +import com.google.android.material.textfield.TextInputLayout; + import at.bitfire.cert4android.CustomCertManager; import butterknife.BindView; import butterknife.ButterKnife; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.persistence.NoteServerSyncHelper; +import it.niedermann.owncloud.notes.util.ExceptionHandler; import it.niedermann.owncloud.notes.util.NotesClientUtil; import it.niedermann.owncloud.notes.util.NotesClientUtil.LoginStatus; @@ -63,6 +66,7 @@ public class SettingsActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this)); setContentView(R.layout.activity_settings); ButterKnife.bind(this); @@ -76,11 +80,8 @@ public class SettingsActivity extends AppCompatActivity { } } - 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.setOnFocusChangeListener((View v, boolean hasFocus) -> { + new URLValidatorAsyncTask().execute(NotesClientUtil.formatURL(field_url.getText().toString())); }); field_url.addTextChangedListener(new TextWatcher() { @@ -128,27 +129,18 @@ public class SettingsActivity extends AppCompatActivity { 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.setOnEditorActionListener((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); - } + field_password.setOnFocusChangeListener((View v, boolean hasFocus) -> { + setPasswordHint(hasFocus); }); setPasswordHint(false); btn_submit.setEnabled(false); - btn_submit.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - login(); - } + btn_submit.setOnClickListener((View v) -> { + login(); }); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SplashscreenActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SplashscreenActivity.java index 295cd193..a8ec91e9 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SplashscreenActivity.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/activity/SplashscreenActivity.java @@ -2,8 +2,11 @@ package it.niedermann.owncloud.notes.android.activity; import android.content.Intent; import android.os.Bundle; + import androidx.appcompat.app.AppCompatActivity; +import it.niedermann.owncloud.notes.util.ExceptionHandler; + /** * Created by stefan on 18.04.17. */ @@ -12,6 +15,7 @@ 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); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/appwidget/NoteListWidget.java b/app/src/main/java/it/niedermann/owncloud/notes/android/appwidget/NoteListWidget.java index 034fbb18..b0b18f33 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/appwidget/NoteListWidget.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/appwidget/NoteListWidget.java @@ -9,6 +9,7 @@ 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 it.niedermann.owncloud.notes.R; @@ -52,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(), R.layout.widget_note_list_dark); @@ -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(R.string.app_name); - case NoteListWidget.NLW_DISPLAY_STARRED: return context.getString(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(R.string.action_uncategorized); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java index aee5bc9e..78abe70d 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/BaseNoteFragment.java @@ -3,7 +3,12 @@ package it.niedermann.owncloud.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.text.SpannableString; @@ -22,13 +27,18 @@ import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.ShareActionProvider; import androidx.core.view.MenuItemCompat; import androidx.core.view.ViewCompat; + import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.android.activity.EditNoteActivity; import it.niedermann.owncloud.notes.model.CloudNote; import it.niedermann.owncloud.notes.model.DBNote; import it.niedermann.owncloud.notes.persistence.NoteSQLiteOpenHelper; import it.niedermann.owncloud.notes.util.DisplayUtils; import it.niedermann.owncloud.notes.util.ICallback; +import static androidx.core.content.pm.ShortcutManagerCompat.isRequestPinShortcutSupported; +import static it.niedermann.owncloud.notes.android.activity.EditNoteActivity.ACTION_SHORTCUT; + public abstract class BaseNoteFragment extends Fragment implements CategoryDialogFragment.CategoryDialogListener { public interface NoteFragmentListener { @@ -37,6 +47,7 @@ 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"; @@ -146,6 +157,10 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo @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 @@ -171,6 +186,7 @@ public abstract class BaseNoteFragment extends Fragment implements CategoryDialo searchEditFrame.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { int oldVisibility = -1; + @Override public void onGlobalLayout() { int currentVisibility = searchEditFrame.getVisibility(); @@ -250,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); } @@ -269,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); @@ -277,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/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java index 7d6f7d83..e27d45e9 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/CategoryDialogFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/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 androidx.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 it.niedermann.owncloud.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/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java index c432a869..07a95a94 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NoteEditFragment.java @@ -1,5 +1,6 @@ package it.niedermann.owncloud.notes.android.fragment; +import android.content.Context; import android.content.SharedPreferences; import android.graphics.Typeface; import android.os.Bundle; @@ -9,18 +10,21 @@ 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; -import androidx.annotation.Nullable; import butterknife.BindView; import butterknife.ButterKnife; import it.niedermann.owncloud.notes.R; @@ -109,43 +113,55 @@ 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()); - setActiveTextView(editContent); + setActiveTextView(editContent); - if (note.getContent().isEmpty()) { - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } + if (note.getContent().isEmpty()) { + editContent.requestFocus(); - // workaround for issue yydcdut/RxMarkdown#41 - note.setContent(note.getContent().replace("\r\n", "\n")); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - editContent.setText(note.getContent()); - editContent.setEnabled(true); + InputMethodManager imm = (InputMethodManager) + getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(getView(), InputMethodManager.SHOW_IMPLICIT); - 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) { - } + // workaround for issue yydcdut/RxMarkdown#41 + note.setContent(note.getContent().replace("\r\n", "\n")); - @Override - public void onNext(CharSequence charSequence) { - editContent.setText(charSequence, TextView.BufferType.SPANNABLE); - } - }); + editContent.setText(note.getContent()); + editContent.setEnabled(true); - editContent.setCustomSelectionActionModeCallback(new StyleCallback(this.editContent)); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - if(sp.getBoolean("font", false)) { - editContent.setTypeface(Typeface.MONOSPACE); + 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"); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java index 07aecb0d..9153ac01 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/NotePreviewFragment.java @@ -6,6 +6,7 @@ import android.os.Bundle; 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; @@ -72,7 +73,7 @@ public class NotePreviewFragment extends BaseNoteFragment { RxMarkdown.with(content, getActivity()) .config( - MarkDownUtil.getMarkDownConfiguration(getActivity().getApplicationContext()) + MarkDownUtil.getMarkDownConfiguration(noteContent.getContext()) /*.setOnTodoClickCallback(new OnTodoClickCallback() { @Override public CharSequence onTodoClicked(View view, String line, int lineNumber) { @@ -136,7 +137,8 @@ public class NotePreviewFragment extends BaseNoteFragment { }); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()); - if (sp.getBoolean("font", false)) { + noteContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(sp)); + if (sp.getBoolean(getString(R.string.pref_key_font), false)) { noteContent.setTypeface(Typeface.MONOSPACE); } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java index 2989b350..ad859115 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/PreferencesFragment.java @@ -1,12 +1,11 @@ package it.niedermann.owncloud.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.util.Log; import android.widget.Toast; import androidx.annotation.Nullable; @@ -21,30 +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()); + themePref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> { + Boolean darkTheme = (Boolean) newValue; + Notes.setAppTheme(darkTheme); + getActivity().setResult(Activity.RESULT_OK); + getActivity().recreate(); + return true; + }); - themePref.setSummary(sp.getBoolean(getString(R.string.pref_key_theme), false) ? - getString(R.string.pref_value_theme_dark) : getString(R.string.pref_value_theme_light)); - 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().recreate(); - - return true; - } + 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/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentContributingTab.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentContributingTab.java index 3d4358d7..4c72608b 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentContributingTab.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentContributingTab.java @@ -1,12 +1,13 @@ package it.niedermann.owncloud.notes.android.fragment.about; import android.os.Bundle; -import androidx.fragment.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 it.niedermann.owncloud.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/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentCreditsTab.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentCreditsTab.java index f725393c..d2b01a7c 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentCreditsTab.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentCreditsTab.java @@ -1,12 +1,13 @@ package it.niedermann.owncloud.notes.android.fragment.about; import android.os.Bundle; -import androidx.fragment.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 it.niedermann.owncloud.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/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java index cf8fe07b..d32b4632 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/fragment/about/AboutFragmentLicenseTab.java @@ -3,13 +3,14 @@ package it.niedermann.owncloud.notes.android.fragment.about; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import androidx.fragment.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/it/niedermann/owncloud/notes/android/quicksettings/NewNoteTileService.java b/app/src/main/java/it/niedermann/owncloud/notes/android/quicksettings/NewNoteTileService.java index af7eb75e..ca101c88 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/android/quicksettings/NewNoteTileService.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/android/quicksettings/NewNoteTileService.java @@ -27,15 +27,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/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java index ac4a9278..c70df682 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/LoadNotesListTask.java @@ -60,15 +60,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(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()) { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java index b92d29cc..43477dd3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteSQLiteOpenHelper.java @@ -3,21 +3,26 @@ package it.niedermann.owncloud.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 androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.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; +import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.android.appwidget.NoteListWidget; import it.niedermann.owncloud.notes.android.appwidget.SingleNoteWidget; import it.niedermann.owncloud.notes.model.CloudNote; @@ -49,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) { @@ -244,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)); @@ -302,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 * @@ -327,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 + "/%"); } @@ -527,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/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java index fdccffde..f6558718 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NoteServerSyncHelper.java @@ -67,6 +67,19 @@ public class NoteServerSyncHelper { // 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) { @@ -105,6 +118,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() { @@ -114,6 +128,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); @@ -210,9 +229,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/it/niedermann/owncloud/notes/util/ExceptionHandler.java b/app/src/main/java/it/niedermann/owncloud/notes/util/ExceptionHandler.java new file mode 100644 index 00000000..8bd9faa9 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/ExceptionHandler.java @@ -0,0 +1,35 @@ +package it.niedermann.owncloud.notes.util; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +import java.io.Serializable; + +import it.niedermann.owncloud.notes.android.activity.ExceptionActivity; + +import static it.niedermann.owncloud.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/it/niedermann/owncloud/notes/util/MarkDownUtil.java b/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java index b1f8ff82..4e89d268 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/util/MarkDownUtil.java @@ -1,6 +1,8 @@ package it.niedermann.owncloud.notes.util; import android.content.Context; + +import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import com.yydcdut.rxmarkdown.RxMDConfiguration; @@ -22,14 +24,15 @@ 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(ResourcesCompat.getColor(context.getResources(), R.color.primary, null)); + .setLinkFontColor(ContextCompat.getColor(context, R.color.primary)); } public static Builder getMarkDownConfiguration(Context context, Boolean darkTheme) { 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 00000000..cc8872c1 --- /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_sync_black_24dp.xml b/app/src/main/res/drawable/ic_sync_black_24dp.xml new file mode 100644 index 00000000..3628dedb --- /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_wifi_black_24dp.xml b/app/src/main/res/drawable/ic_wifi_black_24dp.xml new file mode 100644 index 00000000..d5798991 --- /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_exception.xml b/app/src/main/res/layout/activity_exception.xml new file mode 100644 index 00000000..d512dfa7 --- /dev/null +++ b/app/src/main/res/layout/activity_exception.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + +