diff --git a/build.gradle b/build.gradle index 5db3844b45..a620a386bd 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:7.1.1' classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0" classpath 'com.adarshr:gradle-test-logger-plugin:2.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" diff --git a/gradle.properties b/gradle.properties index 9bd3a242ba..f6350c3043 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,11 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -VERSION_NAME=6.1.0 -VERSION_CODE=301 +VERSION_NAME=6.2.0 +VERSION_CODE=302 PACKAGE=it.feio.android.omninotes MIN_SDK=21 -TARGET_SDK=29 +TARGET_SDK=30 BUILD_TOOLS=29.0.2 android.enableJetifier=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6c9a2f693b..93da2f62e2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip diff --git a/omniNotes/build.gradle b/omniNotes/build.gradle index af3943f145..84c4a7881a 100644 --- a/omniNotes/build.gradle +++ b/omniNotes/build.gradle @@ -36,9 +36,7 @@ if (signConfigExists) { } android { - compileSdkVersion Integer.parseInt(project.TARGET_SDK) -// buildToolsVersion project.BUILD_TOOLS defaultConfig { applicationId project.PACKAGE @@ -143,6 +141,8 @@ dependencies { annotationProcessor 'org.androidannotations:androidannotations:4.8.0' implementation 'org.androidannotations:androidannotations-api:4.8.0' + implementation "androidx.core:core-ktx:1.6.0" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.larswerkman:HoloColorPicker:1.4' implementation 'de.keyboardsurfer.android.widget:crouton:1.8.4@aar' implementation 'com.google.android.apps.dashclock:dashclock-api:2.0.0' @@ -202,30 +202,7 @@ dependencies { betaImplementation 'io.nlopez.smartlocation:library:3.2.4' alphaImplementation 'io.nlopez.smartlocation:library:3.2.4' fossImplementation 'com.github.federicoiosue:smart-location-lib:3.2.6' - - playImplementation('com.github.federicoiosue:analitica:0.0.3:googleAnalyticsRelease@aar') { - transitive = true - } - betaImplementation('com.github.federicoiosue:analitica:0.0.3:googleAnalyticsRelease@aar') { - transitive = true - } - alphaImplementation('com.github.federicoiosue:analitica:0.0.3:googleAnalyticsRelease@aar') { - transitive = true - } - fossImplementation('com.github.federicoiosue:analitica:0.0.3:piwikRelease@aar') { - transitive = true - } - playImplementation 'com.google.android.gms:play-services-analytics:9.0.2' - betaImplementation 'com.google.android.gms:play-services-analytics:9.0.2' - alphaImplementation 'com.google.android.gms:play-services-analytics:9.0.2' - fossImplementation('org.piwik.sdk:piwik-sdk:1.0.2') { - exclude group: 'com.jakewharton.timber', module: 'timber' - } fossImplementation 'com.jakewharton.timber:timber:4.5.1' - - testImplementation 'junit:junit:4.12' - implementation "androidx.core:core-ktx:1.6.0" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } configurations.all { diff --git a/omniNotes/src/androidTest/java/it/feio/android/omninotes/helpers/BackupHelperTest.java b/omniNotes/src/androidTest/java/it/feio/android/omninotes/helpers/BackupHelperTest.java index d8c5112857..6b15fca70a 100644 --- a/omniNotes/src/androidTest/java/it/feio/android/omninotes/helpers/BackupHelperTest.java +++ b/omniNotes/src/androidTest/java/it/feio/android/omninotes/helpers/BackupHelperTest.java @@ -23,7 +23,7 @@ import android.net.Uri; import androidx.test.ext.junit.runners.AndroidJUnit4; import it.feio.android.omninotes.BaseAndroidTestCase; -import it.feio.android.omninotes.RetryableAssert; +import it.feio.android.omninotes.OmniNotes; import it.feio.android.omninotes.exceptions.BackupException; import it.feio.android.omninotes.exceptions.checked.BackupAttachmentException; import it.feio.android.omninotes.models.Attachment; @@ -32,7 +32,6 @@ import it.feio.android.omninotes.utils.StorageHelper; import java.io.File; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.util.Collection; import org.apache.commons.io.FileUtils; @@ -152,6 +151,19 @@ public void importAttachment() throws IOException, BackupAttachmentException { assertTrue(new File(attachment.getUri().getPath()).exists()); } + @Test + public void importSettings_notFound() throws IOException { + BackupHelper.importSettings(backupDir); + } + + @Test + public void importSettings() throws IOException { + new File(backupDir, StorageHelper.getSharedPreferencesFile(OmniNotes.getAppContext()).getName()) + .createNewFile(); + + BackupHelper.importSettings(backupDir); + } + private Attachment createTestAttachmentBackup() throws IOException { File testAttachment = new File(attachmentsBackupDir, "testAttachment"); if (!testAttachment.createNewFile()) { diff --git a/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/CalendarReminderSyncTest.java b/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/CalendarReminderSyncTest.java new file mode 100644 index 0000000000..ceb8bfb767 --- /dev/null +++ b/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/CalendarReminderSyncTest.java @@ -0,0 +1,75 @@ +package it.feio.android.omninotes.ui; + +import androidx.test.espresso.matcher.ViewMatchers; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.runner.RunWith; +import it.feio.android.omninotes.R; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.longClick; +import static androidx.test.espresso.action.ViewActions.scrollTo; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.RootMatchers.isDialog; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withParent; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.not; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class CalendarReminderSyncTest extends BaseEspressoTest { + + @Test + public void syncButtonAppears() throws InterruptedException { + Thread.sleep(1500); + + onView(Matchers.allOf(ViewMatchers.withId(R.id.fab_expand_menu_button), + withParent(withId(R.id.fab)), + isDisplayed())).perform(click()); + + onView(allOf(withId(R.id.fab_note), + withParent(withId(R.id.fab)), + isDisplayed())).perform(click()); + + onView(withId(R.id.reminder_layout)).perform(scrollTo(), click()); + onView(allOf(withId(R.id.buttonPositive), withText("Ok"), isDisplayed())).perform(click()); + onView(withId(R.id.sync_reminder_layout)).check(matches(isDisplayed())); + + Thread.sleep(1500); + + } + + @Test + public void syncButtonDisappears() throws InterruptedException { + Thread.sleep(1500); + + onView(Matchers.allOf(ViewMatchers.withId(R.id.fab_expand_menu_button), + withParent(withId(R.id.fab)), + isDisplayed())).perform(click()); + + onView(allOf(withId(R.id.fab_note), + withParent(withId(R.id.fab)), + isDisplayed())).perform(click()); + + onView(withId(R.id.reminder_layout)).perform(scrollTo(), click()); + onView(allOf(withId(R.id.buttonPositive), withText("Ok"), isDisplayed())).perform(click()); + + onView(withId(R.id.reminder_layout)).perform(scrollTo(), longClick()); + onView(withText("Ok")) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + .perform(click()); + + onView(withId(R.id.sync_reminder_layout)).check(matches(not(isDisplayed()))); + + Thread.sleep(1500); + + } + +} diff --git a/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/MrJingleLifecycleTest.java b/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/MrJingleLifecycleTest.java index 24741f9007..95a1b8f384 100644 --- a/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/MrJingleLifecycleTest.java +++ b/omniNotes/src/androidTest/java/it/feio/android/omninotes/ui/MrJingleLifecycleTest.java @@ -22,6 +22,7 @@ import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withParent; import static androidx.test.espresso.matcher.ViewMatchers.withText; @@ -66,6 +67,7 @@ public void mrJingle_displayedOnLastNoteArchived() { createTestNote("title", "content", 0); archiveNotes(dbHelper.getAllNotes(false), true); + onView(isRoot()).perform(waitId(R.id.empty_list, 2000)); onView(allOf(withId(R.id.empty_list), withText(R.string.no_items_in_list), withParent(withParent(IsInstanceOf.instanceOf(android.widget.FrameLayout.class))), isDisplayed())).check(matches(isDisplayed())); diff --git a/omniNotes/src/androidTest/java/it/feio/android/omninotes/utils/StorageHelperTest.kt b/omniNotes/src/androidTest/java/it/feio/android/omninotes/utils/StorageHelperTest.kt new file mode 100644 index 0000000000..5ea861791c --- /dev/null +++ b/omniNotes/src/androidTest/java/it/feio/android/omninotes/utils/StorageHelperTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013-2022 Federico Iosue (federico@iosue.it) + * + * 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 it.feio.android.omninotes.utils + +import it.feio.android.omninotes.BaseAndroidTestCase +import junit.framework.Assert.assertTrue +import org.junit.Test + +class StorageHelperTest : BaseAndroidTestCase() { + + @Test + fun getOrCreateExternalStoragePublicDir() { + val dir = StorageHelper.getOrCreateExternalStoragePublicDir() + + assertTrue(dir.canRead()) + assertTrue(dir.canWrite()) + } +} \ No newline at end of file diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/AboutActivity.java b/omniNotes/src/main/java/it/feio/android/omninotes/AboutActivity.java index 9ed075b1c3..2ec9999bb9 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/AboutActivity.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/AboutActivity.java @@ -33,21 +33,12 @@ protected void onCreate(Bundle savedInstanceState) { initUI(); } - - @Override - public void onStart() { - ((OmniNotes) getApplication()).getAnalyticsHelper().trackScreenView(getClass().getName()); - super.onStart(); - } - - @Override public boolean onNavigateUp() { onBackPressed(); return true; } - private void initUI() { Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/BaseFragment.java b/omniNotes/src/main/java/it/feio/android/omninotes/BaseFragment.java index e5e3aa6b88..ea34fa8055 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/BaseFragment.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/BaseFragment.java @@ -23,17 +23,9 @@ public class BaseFragment extends Fragment { - private static final long OPTIONS_ITEM_CLICK_DELAY_TIME = 1000; private long mLastClickTime; - @Override - public void onStart() { - super.onStart(); - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper() - .trackScreenView(getClass().getName()); - } - @Override public void onDestroy() { super.onDestroy(); diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/DetailFragment.java b/omniNotes/src/main/java/it/feio/android/omninotes/DetailFragment.java index dcfb3bc4a4..f3ac32aa91 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/DetailFragment.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/DetailFragment.java @@ -85,6 +85,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.provider.CalendarContract; import android.provider.MediaStore; import android.text.Editable; import android.text.Selection; @@ -199,6 +200,7 @@ public class DetailFragment extends BaseFragment implements OnReminderPickedList private static final int CATEGORY = 5; private static final int DETAIL = 6; private static final int FILES = 7; + private static final int GCALENDAR = 8; private FragmentDetailBinding binding; @@ -619,6 +621,7 @@ private void initViewReminder() { .getRecurrenceRule()); }); + binding.fragmentDetailContent.reminderLayout.setOnLongClickListener(v -> { MaterialDialog dialog = new MaterialDialog.Builder(mainActivity) .content(R.string.remove_reminder) @@ -629,6 +632,7 @@ private void initViewReminder() { binding.fragmentDetailContent.reminderIcon .setImageResource(R.drawable.ic_alarm_black_18dp); binding.fragmentDetailContent.datetime.setText(""); + binding.fragmentDetailContent.syncReminderLayout.setVisibility(View.GONE); }).build(); dialog.show(); return true; @@ -641,6 +645,18 @@ private void initViewReminder() { .setImageResource(R.drawable.ic_alarm_add_black_18dp); binding.fragmentDetailContent.datetime.setText(reminderString); } + + //For the sync reminder + binding.fragmentDetailContent.syncReminderLayout.setOnClickListener(v -> { + syncReminder(); + }); + + //Sync Reminder + if (noteTmp.getAlarm() != null) + binding.fragmentDetailContent.syncReminderLayout.setVisibility(View.VISIBLE); + else + binding.fragmentDetailContent.syncReminderLayout.setVisibility(View.GONE); + } private void initViewLocation() { @@ -986,6 +1002,10 @@ public void onPrepareOptionsMenu(Menu menu) { .setVisible(noteTmp.isChecklist() && mChecklistManager.getCheckedCount() > 0); menu.findItem(R.id.menu_lock).setVisible(!noteTmp.isLocked()); menu.findItem(R.id.menu_unlock).setVisible(noteTmp.isLocked()); + + //If a reminder had been set for the note, then the "sync reminder option" will appear + //menu.findItem(R.id.menu_sync_reminder).setVisible(noteTmp.getAlarm() != null); + // If note is trashed only this options will be available from menu if (noteTmp.isTrashed()) { menu.findItem(R.id.menu_untrash).setVisible(true); @@ -1097,12 +1117,24 @@ public boolean onOptionsItemSelected(MenuItem item) { default: LogDelegate.w("Invalid menu option selected"); } + return super.onOptionsItemSelected(item); + } - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper() - .trackActionFromResourceId(getActivity(), - item.getItemId()); + private void syncReminder () { - return super.onOptionsItemSelected(item); + //First save the note, them call the calendar intent + saveNote(this); + + Intent intent = new Intent(Intent.ACTION_INSERT) + .setData(CalendarContract.Events.CONTENT_URI) + .putExtra(CalendarContract.Events.TITLE, noteTmp.getTitle()) + .putExtra(CalendarContract.Events.DESCRIPTION, noteTmp.getContent()) + .putExtra(CalendarContract.Events.EVENT_LOCATION, noteTmp.getAddress()) + .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, parseLong(noteTmp.getAlarm())) + .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, parseLong(noteTmp.getAlarm())) + .putExtra(CalendarContract.Events.RRULE, noteTmp.getRecurrenceRule()); + + startActivity(intent); } private void showNoteInfo() { @@ -2000,6 +2032,8 @@ public void onReminderPicked(long reminder) { binding.fragmentDetailContent.reminderIcon.setImageResource(R.drawable.ic_alarm_black_18dp); binding.fragmentDetailContent.datetime .setText(RecurrenceHelper.getNoteReminderText(reminder)); + + binding.fragmentDetailContent.syncReminderLayout.setVisibility(View.VISIBLE); } } diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/GalleryActivity.java b/omniNotes/src/main/java/it/feio/android/omninotes/GalleryActivity.java index e81c11705e..996008e9b9 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/GalleryActivity.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/GalleryActivity.java @@ -104,12 +104,6 @@ protected void onCreate(Bundle savedInstanceState) { initData(); } - @Override - public void onStart() { - ((OmniNotes) getApplication()).getAnalyticsHelper().trackScreenView(getClass().getName()); - super.onStart(); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/ListFragment.java b/omniNotes/src/main/java/it/feio/android/omninotes/ListFragment.java index 2ee489bec4..5505972fb5 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/ListFragment.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/ListFragment.java @@ -992,10 +992,8 @@ void onActivityResult(int requestCode, final int resultCode, Intent intent) { default: break; } - } - private void checkSortActionPerformed(MenuItem item) { if (item.getGroupId() == MENU_SORT_GROUP_ID) { final String[] arrayDb = getResources().getStringArray(R.array.sortable_columns); @@ -1008,14 +1006,9 @@ private void checkSortActionPerformed(MenuItem item) { toggleSearchLabel(false); // Updates app widgets mainActivity.updateWidgets(); - } else { - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper() - .trackActionFromResourceId(getActivity(), - item.getItemId()); } } - /** * Empties trash deleting all the notes */ diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/OmniNotes.java b/omniNotes/src/main/java/it/feio/android/omninotes/OmniNotes.java index 192a5491cd..abf95f1a15 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/OmniNotes.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/OmniNotes.java @@ -19,19 +19,12 @@ import static it.feio.android.omninotes.utils.Constants.PACKAGE; import static it.feio.android.omninotes.utils.ConstantsBase.PREF_LANG; -import static it.feio.android.omninotes.utils.ConstantsBase.PREF_SEND_ANALYTICS; -import static it.feio.android.omninotes.utils.ConstantsBase.PROPERTIES_PARAMS_SEPARATOR; import android.content.Context; import android.content.res.Configuration; import android.os.StrictMode; import androidx.multidex.MultiDexApplication; import com.pixplicity.easyprefs.library.Prefs; -import it.feio.android.analitica.AnalyticsHelper; -import it.feio.android.analitica.AnalyticsHelperFactory; -import it.feio.android.analitica.MockAnalyticsHelper; -import it.feio.android.analitica.exceptions.AnalyticsInstantiationException; -import it.feio.android.analitica.exceptions.InvalidIdentifierException; import it.feio.android.omninotes.helpers.LanguageHelper; import it.feio.android.omninotes.helpers.notifications.NotificationsHelper; import org.acra.ACRA; @@ -48,7 +41,6 @@ public class OmniNotes extends MultiDexApplication { private static Context mContext; - private AnalyticsHelper analyticsHelper; public static boolean isDebugBuild() { return BuildConfig.BUILD_TYPE.equals("debug"); @@ -96,18 +88,4 @@ public void onConfigurationChanged(Configuration newConfig) { LanguageHelper.updateLanguage(this, language); } - public AnalyticsHelper getAnalyticsHelper() { - if (analyticsHelper == null) { - boolean enableAnalytics = Prefs.getBoolean(PREF_SEND_ANALYTICS, true); - try { - String[] analyticsParams = BuildConfig.ANALYTICS_PARAMS.split(PROPERTIES_PARAMS_SEPARATOR); - analyticsHelper = new AnalyticsHelperFactory().getAnalyticsHelper(this, enableAnalytics, - analyticsParams); - } catch (AnalyticsInstantiationException | InvalidIdentifierException e) { - analyticsHelper = new MockAnalyticsHelper(); - } - } - return analyticsHelper; - } - } diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/SettingsActivity.java b/omniNotes/src/main/java/it/feio/android/omninotes/SettingsActivity.java index 9107b93dd1..69c28c9245 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/SettingsActivity.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/SettingsActivity.java @@ -29,7 +29,6 @@ import com.afollestad.materialdialogs.folderselector.FolderChooserDialog; import de.keyboardsurfer.android.widget.crouton.Crouton; import de.keyboardsurfer.android.widget.crouton.Style; -import it.feio.android.analitica.AnalyticsHelper; import it.feio.android.omninotes.async.DataBackupIntentService; import it.feio.android.omninotes.databinding.ActivitySettingsBinding; import java.io.File; @@ -102,9 +101,6 @@ public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File .content(folder.getName()) .positiveText(R.string.confirm) .onPositive((dialog1, which) -> { - ((OmniNotes) getApplication()).getAnalyticsHelper() - .trackEvent(AnalyticsHelper.CATEGORIES.SETTING, - "settings_import_data"); Intent service = new Intent(getApplicationContext(), DataBackupIntentService.class); service.setAction(DataBackupIntentService.ACTION_DATA_IMPORT_LEGACY); service.putExtra(DataBackupIntentService.INTENT_BACKUP_NAME, folder.getAbsolutePath()); diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/SettingsFragment.java b/omniNotes/src/main/java/it/feio/android/omninotes/SettingsFragment.java index 20ce105de5..5a4f4385f4 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/SettingsFragment.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/SettingsFragment.java @@ -42,7 +42,6 @@ import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; -import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.widget.EditText; @@ -59,7 +58,6 @@ import com.afollestad.materialdialogs.folderselector.FolderChooserDialog; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.pixplicity.easyprefs.library.Prefs; -import it.feio.android.analitica.AnalyticsHelper; import it.feio.android.omninotes.async.DataBackupIntentService; import it.feio.android.omninotes.helpers.AppVersionHelper; import it.feio.android.omninotes.helpers.BackupHelper; @@ -142,18 +140,15 @@ public void onResume() { // Export notes Preference export = findPreference("settings_export_data"); if (export != null) { + export.setSummary(StorageHelper.getOrCreateExternalStoragePublicDir().getAbsolutePath()); export.setOnPreferenceClickListener(arg0 -> { - - // Inflate layout - LayoutInflater inflater = getActivity().getLayoutInflater(); - View v = inflater.inflate(R.layout.dialog_backup_layout, null); + View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_backup_layout, null); // Finds actually saved backups names PermissionsHelper .requestPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, R .string.permission_external_storage, - getActivity().findViewById(R.id.crouton_handle), () -> export - (v)); + getActivity().findViewById(R.id.crouton_handle), () -> export(v)); return false; }); @@ -488,11 +483,6 @@ public void onResume() { Preference changelog = findPreference("settings_changelog"); if (changelog != null) { changelog.setOnPreferenceClickListener(arg0 -> { - - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper() - .trackEvent(AnalyticsHelper.CATEGORIES.SETTING, - "settings_changelog"); - new MaterialDialog.Builder(getContext()) .customView(R.layout.activity_changelog, false) .positiveText(R.string.ok) @@ -555,8 +545,6 @@ public void onResume() { .content(getString(R.string.settings_tour_show_again_summary) + "?") .positiveText(R.string.confirm) .onPositive((dialog, which) -> { - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper().trackEvent( - AnalyticsHelper.CATEGORIES.SETTING, "settings_tour_show_again"); Prefs.edit().putBoolean(PREF_TOUR_COMPLETE, false).apply(); SystemHelper.restartApp(getActivity().getApplicationContext(), MainActivity.class); }).build().show(); @@ -590,10 +578,6 @@ private void importNotes() { .setTitle(R.string.confirm_restoring_backup) .setMessage(message) .setPositiveButton(R.string.confirm, (dialog1, which1) -> { - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper().trackEvent( - AnalyticsHelper.CATEGORIES.SETTING, - "settings_import_data"); - // An IntentService will be launched to accomplish the import task Intent service = new Intent(getActivity(), DataBackupIntentService.class); @@ -666,23 +650,12 @@ public void afterTextChanged(Editable arg0) { .setTitle(R.string.data_export_message) .setView(v) .setPositiveButton(R.string.confirm, (dialog, which) -> { - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper().trackEvent( - AnalyticsHelper.CATEGORIES.SETTING, "settings_export_data"); String backupName = TextUtils.isEmpty(fileNameEditText.getText().toString()) ? fileNameEditText.getHint().toString() : fileNameEditText.getText().toString(); BackupHelper.startBackupService(backupName); }).show(); } - - @Override - public void onStart() { - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper() - .trackScreenView(getClass().getName()); - super.onStart(); - } - - @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (resultCode == Activity.RESULT_OK) { diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/SketchFragment.java b/omniNotes/src/main/java/it/feio/android/omninotes/SketchFragment.java index cab4244f45..32ceec08ab 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/SketchFragment.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/SketchFragment.java @@ -72,15 +72,6 @@ public void onCreate(Bundle savedInstanceState) { setRetainInstance(false); } - - @Override - public void onStart() { - ((OmniNotes) getActivity().getApplication()).getAnalyticsHelper() - .trackScreenView(getClass().getName()); - - super.onStart(); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/async/DataBackupIntentService.java b/omniNotes/src/main/java/it/feio/android/omninotes/async/DataBackupIntentService.java index b65a18678b..81d4834117 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/async/DataBackupIntentService.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/async/DataBackupIntentService.java @@ -39,6 +39,7 @@ import it.feio.android.omninotes.utils.ReminderHelper; import it.feio.android.omninotes.utils.StorageHelper; import java.io.File; +import java.io.IOException; public class DataBackupIntentService extends IntentService implements OnAttachingFileListener { @@ -88,65 +89,61 @@ private void importDataFromSpringpad(Intent intent, NotificationsHelper mNotific } private synchronized void exportData(Intent intent) { - - boolean result; - - // Gets backup folder String backupName = intent.getStringExtra(INTENT_BACKUP_NAME); File backupDir = StorageHelper.getOrCreateBackupDir(backupName); // Directory clean in case of previously used backup name StorageHelper.delete(this, backupDir.getAbsolutePath()); - // Directory is re-created in case of previously used backup name (removed above) backupDir = StorageHelper.getOrCreateBackupDir(backupName); - BackupHelper.exportNotes(backupDir); - result = BackupHelper.exportAttachments(backupDir, mNotificationsHelper); - result = result && BackupHelper.exportSettings(backupDir); - - String notificationMessage = - result ? getString(R.string.data_export_completed) : getString(R.string.data_export_failed); - mNotificationsHelper.finish(intent, notificationMessage); + try { + BackupHelper.exportNotes(backupDir); + BackupHelper.exportAttachments(backupDir, mNotificationsHelper); + BackupHelper.exportSettings(backupDir); + } catch (IOException e) { + e.printStackTrace(); + mNotificationsHelper.finish(intent, getString(R.string.data_export_failed)); + } + mNotificationsHelper.finish(intent, getString(R.string.data_export_completed)); } - private synchronized void importData(Intent intent) { - boolean importLegacy = ACTION_DATA_IMPORT_LEGACY.equals(intent.getAction()); // Gets backup folder String backupName = intent.getStringExtra(INTENT_BACKUP_NAME); File backupDir = importLegacy ? new File(backupName) : StorageHelper.getOrCreateBackupDir(backupName); - BackupHelper.importSettings(backupDir); + try { + BackupHelper.importSettings(backupDir); - if (importLegacy) { - BackupHelper.importDB(this, backupDir); - } else { - BackupHelper.importNotes(backupDir); - } + if (importLegacy) { + BackupHelper.importDB(this, backupDir); + } else { + BackupHelper.importNotes(backupDir); + } - BackupHelper.importAttachments(backupDir, mNotificationsHelper); + BackupHelper.importAttachments(backupDir, mNotificationsHelper); - resetReminders(); + resetReminders(); + mNotificationsHelper.cancel(); - mNotificationsHelper.cancel(); + createNotification(intent, this, getString(R.string.data_import_completed), + getString(R.string.click_to_refresh_application), backupDir); - createNotification(intent, this, getString(R.string.data_import_completed), - getString(R.string.click_to_refresh_application), backupDir); - - // Performs auto-backup filling after backup restore + // Performs auto-backup filling after backup restore // if (Prefs.getBoolean(Constants.PREF_ENABLE_AUTOBACKUP, false)) { // File autoBackupDir = StorageHelper.getBackupDir(Constants.AUTO_BACKUP_DIR); // BackupHelper.exportNotes(autoBackupDir); // BackupHelper.exportAttachments(autoBackupDir); // } + } catch (IOException e) { + mNotificationsHelper.finish(intent, getString(R.string.data_export_failed)); + } } private synchronized void deleteData(Intent intent) { - - // Gets backup folder String backupName = intent.getStringExtra(INTENT_BACKUP_NAME); File backupDir = StorageHelper.getOrCreateBackupDir(backupName); @@ -158,18 +155,17 @@ private synchronized void deleteData(Intent intent) { createNotification(intent, this, title, text, backupDir); } - /** * Creation of notification on operations completed */ - private void createNotification(Intent intent, Context mContext, String title, String message, + private void createNotification(Intent intent, Context context, String title, String message, File backupDir) { // The behavior differs depending on intent action Intent intentLaunch; if (DataBackupIntentService.ACTION_DATA_IMPORT.equals(intent.getAction()) || SpringImportHelper.ACTION_DATA_IMPORT_SPRINGPAD.equals(intent.getAction())) { - intentLaunch = new Intent(mContext, MainActivity.class); + intentLaunch = new Intent(context, MainActivity.class); intentLaunch.setAction(ACTION_RESTART_APP); } else { intentLaunch = new Intent(); @@ -178,10 +174,10 @@ private void createNotification(Intent intent, Context mContext, String title, S intentLaunch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intentLaunch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Creates the PendingIntent - PendingIntent notifyIntent = PendingIntent.getActivity(mContext, 0, intentLaunch, + PendingIntent notifyIntent = PendingIntent.getActivity(context, 0, intentLaunch, PendingIntent.FLAG_UPDATE_CURRENT); - NotificationsHelper notificationsHelper = new NotificationsHelper(mContext); + NotificationsHelper notificationsHelper = new NotificationsHelper(context); notificationsHelper.createStandardNotification(NotificationChannelNames.BACKUPS, R.drawable.ic_content_save_white_24dp, title, notifyIntent) .setMessage(message).setRingtone(Prefs.getString("settings_notification_ringtone", null)) diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/helpers/BackupHelper.java b/omniNotes/src/main/java/it/feio/android/omninotes/helpers/BackupHelper.java index 2a9da731a9..8593d8780f 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/helpers/BackupHelper.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/helpers/BackupHelper.java @@ -46,6 +46,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import lombok.experimental.UtilityClass; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.filefilter.RegexFileFilter; @@ -53,12 +54,9 @@ import org.bitbucket.cowwoc.diffmatchpatch.DiffMatchPatch; import rx.Observable; +@UtilityClass public final class BackupHelper { - private BackupHelper() { - // hides public constructor - } - public static void exportNotes(File backupDir) { for (Note note : DbHelper.getInstance(true).getAllNotes(false)) { exportNote(backupDir, note); @@ -82,26 +80,13 @@ public static File getBackupNoteFile(File backupDir, Note note) { return new File(backupDir, note.get_id() + ".json"); } - /** - * Export attachments to backup folder - * - * @return True if success, false otherwise - */ - public static boolean exportAttachments(File backupDir) { - return exportAttachments(backupDir, null); - } - /** * Export attachments to backup folder notifying for each attachment copied - * - * @return True if success, false otherwise */ - public static boolean exportAttachments(File backupDir, NotificationsHelper notificationsHelper) { - File destinationattachmentsDir = new File(backupDir, - StorageHelper.getAttachmentDir().getName()); - ArrayList list = DbHelper.getInstance().getAllAttachments(); - exportAttachments(notificationsHelper, destinationattachmentsDir, list, null); - return true; + public static void exportAttachments(File backupDir, NotificationsHelper notificationsHelper) { + File attachmentsDestinationDir = new File(backupDir, StorageHelper.getAttachmentDir().getName()); + List list = DbHelper.getInstance().getAllAttachments(); + exportAttachments(notificationsHelper, attachmentsDestinationDir, list, null); } public static boolean exportAttachments(NotificationsHelper notificationsHelper, @@ -244,21 +229,15 @@ public static void startBackupService(String backupFolderName) { OmniNotes.getAppContext().startService(service); } - /** - * Exports settings if required - */ - public static boolean exportSettings(File backupDir) { + public static void exportSettings(File backupDir) throws IOException { File preferences = StorageHelper.getSharedPreferencesFile(OmniNotes.getAppContext()); - return (StorageHelper.copyFile(preferences, new File(backupDir, preferences.getName()))); + StorageHelper.copyFile(preferences, new File(backupDir, preferences.getName()), true); } - /** - * Imports settings - */ - public static boolean importSettings(File backupDir) { + public static void importSettings(File backupDir) throws IOException { File preferences = StorageHelper.getSharedPreferencesFile(OmniNotes.getAppContext()); File preferenceBackup = new File(backupDir, preferences.getName()); - return (StorageHelper.copyFile(preferenceBackup, preferences)); + StorageHelper.copyFile(preferenceBackup, preferences, false); } public static boolean deleteNoteBackup(File backupDir, Note note) { @@ -273,7 +252,6 @@ result && new File(attachmentBackup, FilenameUtils.getName(attachment.getUri().g return result; } - public static void deleteNote(File file) { try { Note note = new Note(); @@ -290,12 +268,11 @@ public static void deleteNote(File file) { * @deprecated {@link BackupHelper#importNotes(File)} */ @Deprecated - public static boolean importDB(Context context, File backupDir) { + public static void importDB(Context context, File backupDir) throws IOException { File database = context.getDatabasePath(DATABASE_NAME); if (database.exists() && database.delete()) { - return (StorageHelper.copyFile(new File(backupDir, DATABASE_NAME), database)); + StorageHelper.copyFile(new File(backupDir, DATABASE_NAME), database, true); } - return false; } public static List> integrityCheck(File backupDir) { diff --git a/omniNotes/src/main/java/it/feio/android/omninotes/utils/StorageHelper.java b/omniNotes/src/main/java/it/feio/android/omninotes/utils/StorageHelper.java index 1db9e09ad1..87da5e4df4 100644 --- a/omniNotes/src/main/java/it/feio/android/omninotes/utils/StorageHelper.java +++ b/omniNotes/src/main/java/it/feio/android/omninotes/utils/StorageHelper.java @@ -20,7 +20,6 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.provider.MediaStore; @@ -43,10 +42,12 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; +import lombok.experimental.UtilityClass; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +@UtilityClass public class StorageHelper { public static boolean checkStorage() { @@ -152,14 +153,13 @@ public static File createExternalStoragePrivateFile(Context mContext, Uri uri, S return file; } - public static boolean copyFile(File source, File destination) { + public static void copyFile(File source, File destination, boolean failOnError) throws IOException { try { FileUtils.copyFile(source, destination); - return true; } catch (IOException e) { LogDelegate.e("Error copying file: " + e.getMessage(), e); + if (failOnError) throw e; } - return false; } /** @@ -274,7 +274,7 @@ public static File getCacheDir(Context mContext) { public static File getOrCreateExternalStoragePublicDir() { - File dir = new File(Environment.getExternalStorageDirectory() + File.separator + File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + File.separator + Constants.EXTERNAL_STORAGE_FOLDER + File.separator); if (!dir.exists() && !dir.mkdirs()) { throw new ExternalDirectoryCreationException("Can't create folder " + dir.getAbsolutePath()); @@ -319,17 +319,11 @@ public static File getSharedPreferencesFile(Context mContext) { /** * Returns a directory size in bytes */ - @SuppressWarnings("deprecation") public static long getSize(File directory) { StatFs statFs = new StatFs(directory.getAbsolutePath()); long blockSize = 0; try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - blockSize = statFs.getBlockSizeLong(); - } else { - blockSize = statFs.getBlockSize(); - } - // Can't understand why on some devices this fails + blockSize = statFs.getBlockSizeLong(); } catch (NoSuchMethodError e) { LogDelegate.e("Mysterious error", e); } @@ -364,30 +358,6 @@ private static long getSize(File directory, long blockSize) { } } - - public static boolean copyDirectory(File sourceLocation, File targetLocation) { - boolean res = true; - - // If target is a directory the method will be iterated - if (sourceLocation.isDirectory()) { - if (!targetLocation.exists()) { - targetLocation.mkdirs(); - } - - String[] children = sourceLocation.list(); - for (int i = 0; i < sourceLocation.listFiles().length; i++) { - res = res && copyDirectory(new File(sourceLocation, children[i]), new File(targetLocation, - children[i])); - } - - // Otherwise a file copy will be performed - } else { - res = copyFile(sourceLocation, targetLocation); - } - return res; - } - - /** * Retrieves uri mime-type using ContentResolver */ diff --git a/omniNotes/src/main/res/drawable/ic_baseline_calendar_today_24.xml b/omniNotes/src/main/res/drawable/ic_baseline_calendar_today_24.xml new file mode 100644 index 0000000000..1cf969c70f --- /dev/null +++ b/omniNotes/src/main/res/drawable/ic_baseline_calendar_today_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/omniNotes/src/main/res/drawable/ic_baseline_sync_24.xml b/omniNotes/src/main/res/drawable/ic_baseline_sync_24.xml new file mode 100644 index 0000000000..c2f773a178 --- /dev/null +++ b/omniNotes/src/main/res/drawable/ic_baseline_sync_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/omniNotes/src/main/res/layout/fragment_detail_content.xml b/omniNotes/src/main/res/layout/fragment_detail_content.xml index 558f850ae0..ac8f502f9b 100644 --- a/omniNotes/src/main/res/layout/fragment_detail_content.xml +++ b/omniNotes/src/main/res/layout/fragment_detail_content.xml @@ -96,4 +96,41 @@ android:textColor="@color/text_color_lighter" pixlui:typeface="Roboto-Regular.ttf" /> + + + + + + + + + diff --git a/omniNotes/src/main/res/raw/changelog.xml b/omniNotes/src/main/res/raw/changelog.xml index 0933365c72..3edbd3c3a2 100644 --- a/omniNotes/src/main/res/raw/changelog.xml +++ b/omniNotes/src/main/res/raw/changelog.xml @@ -16,6 +16,16 @@ --> + + [b]New![/b] Updated to Android 11 to get latest fixes and improvements + IMPORTANT:[/font][/b] This will require to move your old backups to the new directory]]> + + [b]New![/b] Removed all analytics + [u]Fix[/u] Backup import/export error notification + + @@ -27,8 +37,6 @@ [b]New![/b] Add new note from app's icon shortcut [b]New![/b] Move to bottom your checked items on demand [b]New![/b] Added Urdu language - [i]Improved![/i] Updated to Android 11 to include framework fixed and improvements - [i]Improved![/i] Simplified merging notes: confirmation is no more required [i]Improved![/i] More intuitive bulk tag editing: all notes' tags will be diff --git a/omniNotes/src/main/res/values/strings.xml b/omniNotes/src/main/res/values/strings.xml index b8fd7a28ab..ceb289d676 100644 --- a/omniNotes/src/main/res/values/strings.xml +++ b/omniNotes/src/main/res/values/strings.xml @@ -229,6 +229,7 @@ Completed Confirm undoing changes? Done + Sync reminder with Calendar Displays some statistics and expiring reminders from Omni Notes @@ -434,5 +435,7 @@ Notifications for notes\' reminders Pinned Notifications for pinned notes + Remove calendar event? + Calendar event Added diff --git a/omniNotes/src/main/res/xml/settings_privacy.xml b/omniNotes/src/main/res/xml/settings_privacy.xml index cd97c44cb7..aa76d7890d 100644 --- a/omniNotes/src/main/res/xml/settings_privacy.xml +++ b/omniNotes/src/main/res/xml/settings_privacy.xml @@ -27,13 +27,6 @@ android:title="@string/settings_error_reporting" app:iconSpaceReserved="false" /> - -