diff --git a/CHANGELOG.md b/CHANGELOG.md index 595808544..3d6e2f0f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,30 @@ Changelog ========== +Version 3.4.2 *(2018-04-13)* +---------------------------- + + * Hide public notification content if desired so (by fraang) + * Added optional grid on the monthly view + * Allow exporting events on SD cards + * Allow selecting No Sound as a reminder sound + * Set default event status for CalDAV events as Confirmed + +Version 3.4.1 *(2018-03-30)* +---------------------------- + + * Reworked custom notification sound, should be more reliable + * Fixed some glitches related to the monthly view + * Misc smaller bugfixes and stability improvements + +Version 3.4.0 *(2018-02-28)* +---------------------------- + + * Rewrote the monthly view + * Improved the performance at importing events from ics files + * Added many new country holidays + * Handle some new third party intents + Version 3.3.2 *(2018-02-21)* ---------------------------- diff --git a/app/build.gradle b/app/build.gradle index 9ed92cbc7..02719d1fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.simplemobiletools.calendar" minSdkVersion 16 targetSdkVersion 27 - versionCode 115 - versionName "3.3.2" + versionCode 118 + versionName "3.4.2" multiDexEnabled true setProperty("archivesBaseName", "calendar") } @@ -46,11 +46,10 @@ ext { } dependencies { - implementation 'com.simplemobiletools:commons:3.15.6' + implementation 'com.simplemobiletools:commons:3.18.23' implementation 'joda-time:joda-time:2.9.9' implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'com.android.support:multidex:1.0.3' - implementation 'com.google.code.gson:gson:2.8.2' debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c8f601054..4378de2f1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,6 +26,7 @@ @@ -34,7 +35,9 @@ - + @@ -61,6 +64,12 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/MainActivity.kt index bd4d4eb6e..3617d07d5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/MainActivity.kt @@ -63,12 +63,15 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { private var mStoredDayCode = "" private var mStoredIsSundayFirst = false private var mStoredUse24HourFormat = false - private var mStoredUseEnglish = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) appLaunched() + + // just get a reference to the database to make sure it is created properly + dbHelper + checkWhatsNewDialog() calendar_fab.beVisibleIf(config.storedView != YEARLY_VIEW) calendar_fab.setOnClickListener { @@ -83,8 +86,20 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { if (intent?.action == Intent.ACTION_VIEW && intent.data != null) { val uri = intent.data if (uri.authority == "com.android.calendar") { - // clicking date on a third party widget: content://com.android.calendar/time/1507309245683 - if (intent?.extras?.getBoolean("DETAIL_VIEW", false) == true) { + if (uri.path.startsWith("/events")) { + // intents like content://com.android.calendar/events/1756 + val eventId = uri.lastPathSegment + val id = dbHelper.getEventIdWithLastImportId(eventId) + if (id != 0) { + Intent(this, EventActivity::class.java).apply { + putExtra(EVENT_ID, id) + startActivity(this) + } + } else { + toast(R.string.unknown_error_occurred) + } + } else if (intent?.extras?.getBoolean("DETAIL_VIEW", false) == true) { + // clicking date on a third party widget: content://com.android.calendar/time/1507309245683 val timestamp = uri.pathSegments.last() if (timestamp.areDigitsOnly()) { openDayAt(timestamp.toLong()) @@ -111,11 +126,6 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { override fun onResume() { super.onResume() - if (mStoredUseEnglish != config.useEnglish) { - restartActivity() - return - } - if (mStoredTextColor != config.textColor || mStoredBackgroundColor != config.backgroundColor || mStoredPrimaryColor != config.primaryColor || mStoredDayCode != Formatter.getTodayCode(applicationContext)) { updateViewPager() @@ -126,7 +136,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { } if (config.storedView == WEEKLY_VIEW) { - if (mStoredIsSundayFirst != config.isSundayFirst || mStoredUse24HourFormat != config.use24hourFormat) { + if (mStoredIsSundayFirst != config.isSundayFirst || mStoredUse24HourFormat != config.use24HourFormat) { updateViewPager() } } @@ -199,14 +209,19 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { } } + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + setIntent(intent) + checkOpenIntents() + } + private fun storeStateVariables() { config.apply { - mStoredUseEnglish = useEnglish mStoredIsSundayFirst = isSundayFirst mStoredTextColor = textColor mStoredPrimaryColor = primaryColor mStoredBackgroundColor = backgroundColor - mStoredUse24HourFormat = use24hourFormat + mStoredUse24HourFormat = use24HourFormat } mStoredDayCode = Formatter.getTodayCode(applicationContext) } @@ -647,8 +662,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { if (events.isEmpty()) { toast(R.string.no_entries_for_exporting) } else { - toast(R.string.exporting) - IcsExporter().exportEvents(this, file, events as ArrayList) { + IcsExporter().exportEvents(this, file, events as ArrayList, true) { toast(when (it) { IcsExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful IcsExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed @@ -673,7 +687,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { FAQItem(getString(R.string.faq_1_title), getString(R.string.faq_1_text)), FAQItem(getString(R.string.faq_2_title), getString(R.string.faq_2_text))) - startAboutActivity(R.string.app_name, LICENSE_KOTLIN or LICENSE_JODA or LICENSE_STETHO or LICENSE_MULTISELECT or LICENSE_LEAK_CANARY, + startAboutActivity(R.string.app_name, LICENSE_JODA or LICENSE_STETHO or LICENSE_MULTISELECT or LICENSE_LEAK_CANARY, BuildConfig.VERSION_NAME, faqItems) } @@ -730,7 +744,10 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { put("Bolivia", "bolivia.ics") put("Brasil", "brazil.ics") put("Canada", "canada.ics") + put("China", "china.ics") + put("Colombia", "colombia.ics") put("Česká republika", "czech.ics") + put("Danmark", "denmark.ics") put("Deutschland", "germany.ics") put("Eesti", "estonia.ics") put("España", "spain.ics") @@ -738,10 +755,17 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { put("France", "france.ics") put("Hanguk", "southkorea.ics") put("Hellas", "greece.ics") + put("Hrvatska", "croatia.ics") put("India", "india.ics") + put("Indonesia", "indonesia.ics") put("Ísland", "iceland.ics") put("Italia", "italy.ics") + put("Latvija", "latvia.ics") + put("Lietuva", "lithuania.ics") + put("Luxemburg", "luxembourg.ics") + put("Makedonija", "macedonia.ics") put("Magyarország", "hungary.ics") + put("México", "mexico.ics") put("Nederland", "netherlands.ics") put("日本", "japan.ics") put("Norge", "norway.ics") @@ -750,11 +774,15 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { put("Polska", "poland.ics") put("Portugal", "portugal.ics") put("Россия", "russia.ics") + put("România", "romania.ics") put("Schweiz", "switzerland.ics") + put("Srbija", "serbia.ics") put("Slovenija", "slovenia.ics") put("Slovensko", "slovakia.ics") + put("South Africa", "southafrica.ics") put("Suomi", "finland.ics") put("Sverige", "sweden.ics") + put("Ukraine", "ukraine.ics") put("United Kingdom", "unitedkingdom.ics") put("United States", "unitedstates.ics") @@ -794,6 +822,7 @@ class MainActivity : SimpleActivity(), RefreshRecyclerViewListener { add(Release(86, R.string.release_86)) add(Release(88, R.string.release_88)) add(Release(98, R.string.release_98)) + add(Release(117, R.string.release_117)) checkWhatsNew(this, BuildConfig.VERSION_CODE) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/ManageEventTypesActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/ManageEventTypesActivity.kt index 02829afcc..808c50369 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/ManageEventTypesActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/ManageEventTypesActivity.kt @@ -35,7 +35,6 @@ class ManageEventTypesActivity : SimpleActivity(), DeleteEventTypesListener { val adapter = ManageEventTypesAdapter(this, it, this, manage_event_types_list) { showEventTypeDialog(it as EventType) } - adapter.setupDragListener(true) manage_event_types_list.adapter = adapter } } @@ -62,11 +61,13 @@ class ManageEventTypesActivity : SimpleActivity(), DeleteEventTypesListener { } } - dbHelper.deleteEventTypes(eventTypes, deleteEvents) { - if (it == 0) { - toast(R.string.unknown_error_occurred) + Thread { + dbHelper.deleteEventTypes(eventTypes, deleteEvents) { + if (it == 0) { + toast(R.string.unknown_error_occurred) + } } - } + }.start() return true } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SettingsActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SettingsActivity.kt index c91c6fe72..7fb8743bd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SettingsActivity.kt @@ -2,12 +2,9 @@ package com.simplemobiletools.calendar.activities import android.content.Intent import android.content.res.Resources -import android.media.RingtoneManager -import android.net.Uri +import android.media.AudioManager import android.os.Bundle -import android.os.Parcelable import android.text.TextUtils -import com.simplemobiletools.calendar.BuildConfig import com.simplemobiletools.calendar.R import com.simplemobiletools.calendar.dialogs.SelectCalendarsDialog import com.simplemobiletools.calendar.extensions.* @@ -19,12 +16,14 @@ import com.simplemobiletools.calendar.models.EventType import com.simplemobiletools.commons.dialogs.ConfirmationDialog import com.simplemobiletools.commons.dialogs.CustomIntervalPickerDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog +import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.helpers.ALARM_SOUND_TYPE_NOTIFICATION import com.simplemobiletools.commons.helpers.PERMISSION_READ_CALENDAR import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CALENDAR +import com.simplemobiletools.commons.models.AlarmSound import com.simplemobiletools.commons.models.RadioItem import kotlinx.android.synthetic.main.activity_settings.* -import java.io.File import java.util.* class SettingsActivity : SimpleActivity() { @@ -53,6 +52,7 @@ class SettingsActivity : SimpleActivity() { setupDeleteAllEvents() setupReplaceDescription() setupWeekNumbers() + setupShowGrid() setupWeeklyStart() setupWeeklyEnd() setupVibrate() @@ -102,7 +102,7 @@ class SettingsActivity : SimpleActivity() { settings_use_english_holder.setOnClickListener { settings_use_english.toggle() config.useEnglish = settings_use_english.isChecked - useEnglishToggled() + System.exit(0) } } @@ -113,10 +113,10 @@ class SettingsActivity : SimpleActivity() { } private fun setupHourFormat() { - settings_hour_format.isChecked = config.use24hourFormat + settings_hour_format.isChecked = config.use24HourFormat settings_hour_format_holder.setOnClickListener { settings_hour_format.toggle() - config.use24hourFormat = settings_hour_format.isChecked + config.use24HourFormat = settings_hour_format.isChecked } } @@ -268,36 +268,45 @@ class SettingsActivity : SimpleActivity() { } private fun setupWeekNumbers() { - settings_week_numbers.isChecked = config.displayWeekNumbers + settings_week_numbers.isChecked = config.showWeekNumbers settings_week_numbers_holder.setOnClickListener { settings_week_numbers.toggle() - config.displayWeekNumbers = settings_week_numbers.isChecked + config.showWeekNumbers = settings_week_numbers.isChecked + } + } + + private fun setupShowGrid() { + settings_show_grid.isChecked = config.showGrid + settings_show_grid_holder.setOnClickListener { + settings_show_grid.toggle() + config.showGrid = settings_show_grid.isChecked } } private fun setupReminderSound() { - val noRingtone = res.getString(R.string.no_ringtone_selected) - if (config.reminderSound.isEmpty()) { - settings_reminder_sound.text = noRingtone - } else { - settings_reminder_sound.text = RingtoneManager.getRingtone(this, Uri.parse(config.reminderSound))?.getTitle(this) ?: noRingtone - } + settings_reminder_sound.text = config.reminderSoundTitle settings_reminder_sound_holder.setOnClickListener { - Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { - putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION) - putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, res.getString(R.string.reminder_sound)) - putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(config.reminderSound)) - - if (resolveActivity(packageManager) != null) - startActivityForResult(this, GET_RINGTONE_URI) - else { - toast(R.string.no_ringtone_picker) + SelectAlarmSoundDialog(this, config.reminderSoundUri, AudioManager.STREAM_NOTIFICATION, GET_RINGTONE_URI, ALARM_SOUND_TYPE_NOTIFICATION, false, + onAlarmPicked = { + if (it != null) { + updateReminderSound(it) + } + }, onAlarmSoundDeleted = { + if (it.uri == config.reminderSoundUri) { + val defaultAlarm = getDefaultAlarmSound(ALARM_SOUND_TYPE_NOTIFICATION) + updateReminderSound(defaultAlarm) } - } + }) } } + private fun updateReminderSound(alarmSound: AlarmSound) { + config.reminderSoundTitle = alarmSound.title + config.reminderSoundUri = alarmSound.uri + settings_reminder_sound.text = alarmSound.title + } + private fun setupVibrate() { settings_vibrate.isChecked = config.vibrateOnReminder settings_vibrate_holder.setOnClickListener { @@ -319,8 +328,8 @@ class SettingsActivity : SimpleActivity() { private fun setupSnoozeTime() { updateSnoozeTime() settings_snooze_time_holder.setOnClickListener { - showPickIntervalDialog(config.snoozeTime, true) { - config.snoozeTime = it + showPickSecondsDialogHelper(config.snoozeTime, true) { + config.snoozeTime = it / 60 updateSnoozeTime() } } @@ -381,24 +390,10 @@ class SettingsActivity : SimpleActivity() { }) override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { - if (resultCode == RESULT_OK) { - if (requestCode == GET_RINGTONE_URI) { - var uri = resultData?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) - - if (uri == null) { - config.reminderSound = "" - } else { - try { - if ((uri as Uri).scheme == "file") { - uri = getFilePublicUri(File(uri.path), BuildConfig.APPLICATION_ID) - } - settings_reminder_sound.text = RingtoneManager.getRingtone(this, uri)?.getTitle(this) - config.reminderSound = uri.toString() - } catch (e: Exception) { - showErrorToast(e) - } - } - } + super.onActivityResult(requestCode, resultCode, resultData) + if (requestCode == GET_RINGTONE_URI && resultCode == RESULT_OK && resultData != null) { + val newAlarmSound = storeNewYourAlarmSound(resultData) + updateReminderSound(newAlarmSound) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SnoozeReminderActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SnoozeReminderActivity.kt index 75f7c0431..455a6313d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SnoozeReminderActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/SnoozeReminderActivity.kt @@ -6,17 +6,17 @@ import com.simplemobiletools.calendar.extensions.config import com.simplemobiletools.calendar.extensions.dbHelper import com.simplemobiletools.calendar.extensions.rescheduleReminder import com.simplemobiletools.calendar.helpers.EVENT_ID -import com.simplemobiletools.commons.extensions.showPickIntervalDialog +import com.simplemobiletools.commons.extensions.showPickSecondsDialogHelper class SnoozeReminderActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - showPickIntervalDialog(config.snoozeTime, true, cancelCallback = { dialogCancelled() }) { + showPickSecondsDialogHelper(config.snoozeTime, true, cancelCallback = { dialogCancelled() }) { val eventId = intent.getIntExtra(EVENT_ID, 0) val event = dbHelper.getEventWithId(eventId) - config.snoozeTime = it - rescheduleReminder(event, it) + config.snoozeTime = it / 60 + rescheduleReminder(event, it / 60) finishActivity() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetListConfigureActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetListConfigureActivity.kt index e852a590d..90727f041 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetListConfigureActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetListConfigureActivity.kt @@ -106,15 +106,15 @@ class WidgetListConfigureActivity : SimpleActivity() { } private fun pickBackgroundColor() { - ColorPickerDialog(this, mBgColorWithoutTransparency) { - mBgColorWithoutTransparency = it + ColorPickerDialog(this, mBgColorWithoutTransparency) { wasPositivePressed, color -> + mBgColorWithoutTransparency = color updateBgColor() } } private fun pickTextColor() { - ColorPickerDialog(this, mTextColor) { - mTextColorWithoutTransparency = it + ColorPickerDialog(this, mTextColor) { wasPositivePressed, color -> + mTextColorWithoutTransparency = color updateColors() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetMonthlyConfigureActivity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetMonthlyConfigureActivity.kt index 311976787..c391626b7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetMonthlyConfigureActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/activities/WidgetMonthlyConfigureActivity.kt @@ -88,7 +88,7 @@ class WidgetMonthlyConfigureActivity : SimpleActivity(), MonthlyCalendar { config_bg_seekbar.progress = (mBgAlpha * 100).toInt() updateBgColor() - MonthlyCalendarImpl(this, applicationContext).updateMonthlyCalendar(DateTime(), false) + MonthlyCalendarImpl(this, applicationContext).updateMonthlyCalendar(DateTime().withDayOfMonth(1), false) } private fun saveConfig() { @@ -110,15 +110,15 @@ class WidgetMonthlyConfigureActivity : SimpleActivity(), MonthlyCalendar { } private fun pickBackgroundColor() { - ColorPickerDialog(this, mBgColorWithoutTransparency) { - mBgColorWithoutTransparency = it + ColorPickerDialog(this, mBgColorWithoutTransparency) { wasPositivePressed, color -> + mBgColorWithoutTransparency = color updateBgColor() } } private fun pickTextColor() { - ColorPickerDialog(this, mTextColor) { - mTextColorWithoutTransparency = it + ColorPickerDialog(this, mTextColor) { wasPositivePressed, color -> + mTextColorWithoutTransparency = color updateColors() updateDays() } @@ -154,7 +154,7 @@ class WidgetMonthlyConfigureActivity : SimpleActivity(), MonthlyCalendar { private fun updateDays() { val len = mDays!!.size - if (applicationContext.config.displayWeekNumbers) { + if (applicationContext.config.showWeekNumbers) { week_num.setTextColor(mTextColor) week_num.beVisible() @@ -194,7 +194,7 @@ class WidgetMonthlyConfigureActivity : SimpleActivity(), MonthlyCalendar { } } - override fun updateMonthlyCalendar(context: Context, month: String, days: List, checkedEvents: Boolean) { + override fun updateMonthlyCalendar(context: Context, month: String, days: ArrayList, checkedEvents: Boolean) { runOnUiThread { mDays = days top_value.text = month diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/DayEventsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/DayEventsAdapter.kt index 733ae78c8..2343f3567 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/DayEventsAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/DayEventsAdapter.kt @@ -24,6 +24,10 @@ class DayEventsAdapter(activity: SimpleActivity, val events: ArrayList, r private var allDayString = resources.getString(R.string.all_day) private var replaceDescriptionWithLocation = activity.config.replaceDescription + init { + setupDragListener(true) + } + override fun getActionMenuId() = R.menu.cab_day override fun prepareActionMode(menu: Menu) {} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/EventListAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/EventListAdapter.kt index df29b56fc..8d3a8c29a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/EventListAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/adapters/EventListAdapter.kt @@ -35,7 +35,7 @@ class EventListAdapter(activity: SimpleActivity, val listItems: ArrayList, val listener: DeleteEventTypesListener?, recyclerView: MyRecyclerView, itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, null, itemClick) { + init { + setupDragListener(true) + } + override fun getActionMenuId() = R.menu.cab_event_type override fun prepareActionMode(menu: Menu) {} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/DeleteEventDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/DeleteEventDialog.kt index 9654b4885..5c88fc256 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/DeleteEventDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/DeleteEventDialog.kt @@ -29,8 +29,8 @@ class DeleteEventDialog(val activity: Activity, eventIds: List, val callbac .setPositiveButton(R.string.yes, { dialog, which -> dialogConfirmed(view as ViewGroup, hasRepeatableEvent) }) .setNegativeButton(R.string.no, null) .create().apply { - activity.setupDialogStuff(view, this) - } + activity.setupDialogStuff(view, this) + } } private fun dialogConfirmed(view: ViewGroup, hasRepeatableEvent: Boolean) { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ExportEventsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ExportEventsDialog.kt index f478833f2..37f487556 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ExportEventsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ExportEventsDialog.kt @@ -39,26 +39,26 @@ class ExportEventsDialog(val activity: SimpleActivity, val path: String, val cal .setPositiveButton(R.string.ok, null) .setNegativeButton(R.string.cancel, null) .create().apply { - activity.setupDialogStuff(view, this, R.string.export_events) { - getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - val filename = view.export_events_filename.value - when { - filename.isEmpty() -> activity.toast(R.string.empty_name) - filename.isAValidFilename() -> { - val file = File(path, "$filename.ics") - if (file.exists()) { - activity.toast(R.string.name_taken) - return@setOnClickListener - } + activity.setupDialogStuff(view, this, R.string.export_events) { + getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + val filename = view.export_events_filename.value + when { + filename.isEmpty() -> activity.toast(R.string.empty_name) + filename.isAValidFilename() -> { + val file = File(path, "$filename.ics") + if (file.exists()) { + activity.toast(R.string.name_taken) + return@setOnClickListener + } - val eventTypes = (view.export_events_types_list.adapter as FilterEventTypeAdapter).getSelectedItemsSet() - callback(view.export_events_checkbox.isChecked, file, eventTypes) - dismiss() + val eventTypes = (view.export_events_types_list.adapter as FilterEventTypeAdapter).getSelectedItemsSet() + callback(view.export_events_checkbox.isChecked, file, eventTypes) + dismiss() + } + else -> activity.toast(R.string.invalid_name) + } } - else -> activity.toast(R.string.invalid_name) } } - } - } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/FilterEventTypesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/FilterEventTypesDialog.kt index 71c24a62d..e609c84be 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/FilterEventTypesDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/FilterEventTypesDialog.kt @@ -22,8 +22,8 @@ class FilterEventTypesDialog(val activity: SimpleActivity, val callback: () -> U .setPositiveButton(R.string.ok, { dialogInterface, i -> confirmEventTypes() }) .setNegativeButton(R.string.cancel, null) .create().apply { - activity.setupDialogStuff(view, this, R.string.filter_events_by_type) - } + activity.setupDialogStuff(view, this, R.string.filter_events_by_type) + } } private fun confirmEventTypes() { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ImportEventsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ImportEventsDialog.kt index 0b6993ed8..d7ef3f293 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ImportEventsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/ImportEventsDialog.kt @@ -34,17 +34,17 @@ class ImportEventsDialog(val activity: SimpleActivity, val path: String, val cal .setPositiveButton(R.string.ok, null) .setNegativeButton(R.string.cancel, null) .create().apply { - activity.setupDialogStuff(view, this, R.string.import_events) { - getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { - activity.toast(R.string.importing) - Thread { - val result = IcsImporter(activity).importEvents(path, currEventTypeId, currEventTypeCalDAVCalendarId) - handleParseResult(result) - dismiss() - }.start() + activity.setupDialogStuff(view, this, R.string.import_events) { + getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + activity.toast(R.string.importing) + Thread { + val result = IcsImporter(activity).importEvents(path, currEventTypeId, currEventTypeCalDAVCalendarId) + handleParseResult(result) + dismiss() + }.start() + } + } } - } - } } private fun updateEventType(view: ViewGroup) { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectCalendarsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectCalendarsDialog.kt index bb8f43a9d..44b9915e1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectCalendarsDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectCalendarsDialog.kt @@ -36,8 +36,8 @@ class SelectCalendarsDialog(val activity: SimpleActivity, val callback: () -> Un .setPositiveButton(R.string.ok, { dialogInterface, i -> confirmSelection() }) .setNegativeButton(R.string.cancel, null) .create().apply { - activity.setupDialogStuff(view, this, R.string.select_caldav_calendars) - } + activity.setupDialogStuff(view, this, R.string.select_caldav_calendars) + } } private fun addCalendarItem(isEvent: Boolean, text: String, tag: Int = 0, shouldCheck: Boolean = false) { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectEventCalendarDialog.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectEventCalendarDialog.kt index 3a6ecc790..b442a5dda 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectEventCalendarDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/dialogs/SelectEventCalendarDialog.kt @@ -39,8 +39,8 @@ class SelectEventCalendarDialog(val activity: Activity, val calendars: List + eventType!!.color = color setupColor(type_color) } } else { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Activity.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Activity.kt index 4acabf970..600770afb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Activity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Activity.kt @@ -23,7 +23,7 @@ fun BaseSimpleActivity.shareEvents(ids: List) { } val events = dbHelper.getEventsWithIds(ids) - IcsExporter().exportEvents(this, file, events) { + IcsExporter().exportEvents(this, file, events, false) { if (it == IcsExporter.ExportResult.EXPORT_OK) { sharePathIntent(file.absolutePath, BuildConfig.APPLICATION_ID) } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Context.kt index 2dc798663..98f689d9b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/extensions/Context.kt @@ -10,16 +10,18 @@ import android.content.Context import android.content.Intent import android.content.res.Resources import android.database.ContentObserver +import android.media.AudioAttributes +import android.media.AudioManager import android.net.Uri import android.os.Bundle import android.provider.CalendarContract +import android.support.v4.app.AlarmManagerCompat import android.support.v4.app.NotificationCompat import android.view.Gravity import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.TextView -import com.simplemobiletools.calendar.BuildConfig import com.simplemobiletools.calendar.R import com.simplemobiletools.calendar.activities.EventActivity import com.simplemobiletools.calendar.activities.SimpleActivity @@ -31,14 +33,11 @@ import com.simplemobiletools.calendar.receivers.CalDAVSyncReceiver import com.simplemobiletools.calendar.receivers.NotificationReceiver import com.simplemobiletools.calendar.services.SnoozeService import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.helpers.isKitkatPlus -import com.simplemobiletools.commons.helpers.isLollipopPlus -import com.simplemobiletools.commons.helpers.isMarshmallowPlus +import com.simplemobiletools.commons.helpers.SILENT import com.simplemobiletools.commons.helpers.isOreoPlus import org.joda.time.DateTime import org.joda.time.DateTimeZone import org.joda.time.LocalDate -import java.io.File import java.text.SimpleDateFormat import java.util.* @@ -49,12 +48,11 @@ val Context.dbHelper: DBHelper get() = DBHelper.newInstance(applicationContext) fun Context.getNowSeconds() = (System.currentTimeMillis() / 1000).toInt() fun Context.updateWidgets() { - val widgetsCnt = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetMonthlyProvider::class.java)) - if (widgetsCnt.isNotEmpty()) { - val ids = intArrayOf(R.xml.widget_monthly_info) + val widgetIDs = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetMonthlyProvider::class.java)) + if (widgetIDs.isNotEmpty()) { Intent(applicationContext, MyWidgetMonthlyProvider::class.java).apply { action = AppWidgetManager.ACTION_APPWIDGET_UPDATE - putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids) + putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIDs) sendBroadcast(this) } } @@ -63,12 +61,11 @@ fun Context.updateWidgets() { } fun Context.updateListWidget() { - val widgetsCnt = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetListProvider::class.java)) - if (widgetsCnt.isNotEmpty()) { - val ids = intArrayOf(R.xml.widget_list_info) + val widgetIDs = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetListProvider::class.java)) + if (widgetIDs.isNotEmpty()) { Intent(applicationContext, MyWidgetListProvider::class.java).apply { action = AppWidgetManager.ACTION_APPWIDGET_UPDATE - putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids) + putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIDs) sendBroadcast(this) } } @@ -109,12 +106,7 @@ fun Context.scheduleEventIn(notifTS: Long, event: Event) { val pendingIntent = getNotificationIntent(applicationContext, event) val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager - - when { - isMarshmallowPlus() -> alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, notifTS, pendingIntent) - isKitkatPlus() -> alarmManager.setExact(AlarmManager.RTC_WAKEUP, notifTS, pendingIntent) - else -> alarmManager.set(AlarmManager.RTC_WAKEUP, notifTS, pendingIntent) - } + AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, AlarmManager.RTC_WAKEUP, notifTS, pendingIntent) } fun Context.cancelNotification(id: Int) { @@ -159,69 +151,77 @@ fun Context.notifyEvent(event: Event) { val startTime = Formatter.getTimeFromTS(applicationContext, event.startTS) val endTime = Formatter.getTimeFromTS(applicationContext, event.endTS) val startDate = Formatter.getDateFromTS(event.startTS) - val displayedStartDate: String - if (startDate == LocalDate.now()) { - displayedStartDate = "" - } else if (startDate == LocalDate.now().plusDays(1)) { - displayedStartDate = getString(R.string.tomorrow) - } else /* At least 2 days in the future */ { - displayedStartDate = Formatter.getDayAndMonth(startDate) + val displayedStartDate = when (startDate) { + LocalDate.now() -> "" + LocalDate.now().plusDays(1) -> getString(R.string.tomorrow) + else -> "${Formatter.getDateFromCode(this, Formatter.getDayCodeFromTS(event.startTS))}," } + val timeRange = if (event.getIsAllDay()) getString(R.string.all_day) else getFormattedEventTime(startTime, endTime) val descriptionOrLocation = if (config.replaceDescription) event.location else event.description - val content = arrayOf(displayedStartDate, timeRange, descriptionOrLocation).joinToString(" ") - val notification = getNotification(applicationContext, pendingIntent, event, content) + val content = "$displayedStartDate $timeRange $descriptionOrLocation".trim() + val notification = getNotification(pendingIntent, event, content) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(event.id, notification) } @SuppressLint("NewApi") -private fun getNotification(context: Context, pendingIntent: PendingIntent, event: Event, content: String): Notification { - val channelId = "reminder_channel" +fun Context.getNotification(pendingIntent: PendingIntent, event: Event, content: String, publicVersion: Boolean = false): Notification { + var soundUri = config.reminderSoundUri + if (soundUri == SILENT) { + soundUri = "" + } else { + grantReadUriPermission(soundUri) + } + + val channelId = "my_reminder_channel_$soundUri" if (isOreoPlus()) { - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val name = context.resources.getString(R.string.event_reminders) + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_NOTIFICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setLegacyStreamType(AudioManager.STREAM_NOTIFICATION) + .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) + .build() + + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val name = resources.getString(R.string.event_reminders) val importance = NotificationManager.IMPORTANCE_HIGH NotificationChannel(channelId, name, importance).apply { enableLights(true) lightColor = event.color enableVibration(false) + setSound(Uri.parse(soundUri), audioAttributes) notificationManager.createNotificationChannel(this) } } - var soundUri = Uri.parse(context.config.reminderSound) - if (soundUri.scheme == "file") { - try { - soundUri = context.getFilePublicUri(File(soundUri.path), BuildConfig.APPLICATION_ID) - } catch (ignored: Exception) { - } - } + val contentTitle = if (publicVersion) resources.getString(R.string.app_name) else event.title + val contentText = if (publicVersion) resources.getString(R.string.public_event_notification_text) else content - val builder = NotificationCompat.Builder(context) - .setContentTitle(event.title) - .setContentText(content) + val builder = NotificationCompat.Builder(this) + .setContentTitle(contentTitle) + .setContentText(contentText) .setSmallIcon(R.drawable.ic_calendar) .setContentIntent(pendingIntent) .setPriority(Notification.PRIORITY_HIGH) .setDefaults(Notification.DEFAULT_LIGHTS) .setAutoCancel(true) - .setSound(soundUri) + .setSound(Uri.parse(soundUri), AudioManager.STREAM_NOTIFICATION) .setChannelId(channelId) - .addAction(R.drawable.ic_snooze, context.getString(R.string.snooze), getSnoozePendingIntent(context, event)) + .addAction(R.drawable.ic_snooze, getString(R.string.snooze), getSnoozePendingIntent(this, event)) - if (isLollipopPlus()) { - builder.setVisibility(Notification.VISIBILITY_PUBLIC) + if (config.vibrateOnReminder) { + builder.setVibrate(longArrayOf(0, 300, 300, 300)) } - if (context.config.vibrateOnReminder) { - builder.setVibrate(longArrayOf(0, 300, 300, 300)) + if (!publicVersion) { + builder.setPublicVersion(getNotification(pendingIntent, event, content, true)) } return builder.build() } -private fun getFormattedEventTime(startTime: String, endTime: String) = if (startTime == endTime) startTime else "$startTime\u2013$endTime" +private fun getFormattedEventTime(startTime: String, endTime: String) = if (startTime == endTime) startTime else "$startTime \u2013 $endTime" private fun getPendingIntent(context: Context, event: Event): PendingIntent { val intent = Intent(context, EventActivity::class.java) diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/DayFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/DayFragment.kt index 4e2b04304..52e8d4d8f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/DayFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/DayFragment.kt @@ -94,8 +94,8 @@ class DayFragment : Fragment() { .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.ok) { dialog, which -> positivePressed(dateTime, datePicker) } .create().apply { - activity?.setupDialogStuff(view, this) - } + activity?.setupDialogStuff(view, this) + } } private fun positivePressed(dateTime: DateTime, datePicker: DatePicker) { @@ -123,7 +123,7 @@ class DayFragment : Fragment() { lastHash = newHash val replaceDescription = context!!.config.replaceDescription - val sorted = ArrayList(filtered.sortedWith(compareBy({ it.startTS }, { it.endTS }, { it.title }, { + val sorted = ArrayList(filtered.sortedWith(compareBy({ !it.getIsAllDay() }, { it.startTS }, { it.endTS }, { it.title }, { if (replaceDescription) it.location else it.description }))) @@ -139,7 +139,6 @@ class DayFragment : Fragment() { DayEventsAdapter(activity as SimpleActivity, events, mHolder.day_events) { editEvent(it as Event) }.apply { - setupDragListener(true) addVerticalDividers(true) mHolder.day_events.adapter = this } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/EventListFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/EventListFragment.kt index 1ebc35680..34e312867 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/EventListFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/EventListFragment.kt @@ -36,7 +36,7 @@ class EventListFragment : MyFragmentHolder(), RefreshRecyclerViewListener { mView.calendar_empty_list_placeholder.text = placeholderText mView.background = ColorDrawable(context!!.config.backgroundColor) mView.calendar_events_list_holder?.id = (System.currentTimeMillis() % 100000).toInt() - use24HourFormat = context!!.config.use24hourFormat + use24HourFormat = context!!.config.use24HourFormat updateActionBarTitle() return mView } @@ -44,7 +44,7 @@ class EventListFragment : MyFragmentHolder(), RefreshRecyclerViewListener { override fun onResume() { super.onResume() checkEvents() - val use24Hour = context!!.config.use24hourFormat + val use24Hour = context!!.config.use24HourFormat if (use24Hour != use24HourFormat) { use24HourFormat = use24Hour (mView.calendar_events_list.adapter as? EventListAdapter)?.toggle24HourFormat(use24HourFormat) @@ -53,7 +53,7 @@ class EventListFragment : MyFragmentHolder(), RefreshRecyclerViewListener { override fun onPause() { super.onPause() - use24HourFormat = context!!.config.use24hourFormat + use24HourFormat = context!!.config.use24HourFormat } private fun checkEvents() { diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragment.kt index 6c43546d5..2db429489 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragment.kt @@ -9,13 +9,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.DatePicker -import android.widget.LinearLayout import android.widget.RelativeLayout -import android.widget.TextView import com.simplemobiletools.calendar.R import com.simplemobiletools.calendar.activities.MainActivity -import com.simplemobiletools.calendar.extensions.addDayEvents -import com.simplemobiletools.calendar.extensions.addDayNumber import com.simplemobiletools.calendar.extensions.config import com.simplemobiletools.calendar.helpers.Config import com.simplemobiletools.calendar.helpers.DAY_CODE @@ -24,8 +20,10 @@ import com.simplemobiletools.calendar.helpers.MonthlyCalendarImpl import com.simplemobiletools.calendar.interfaces.MonthlyCalendar import com.simplemobiletools.calendar.interfaces.NavigationListener import com.simplemobiletools.calendar.models.DayMonthly -import com.simplemobiletools.commons.extensions.* -import kotlinx.android.synthetic.main.first_row.* +import com.simplemobiletools.commons.extensions.applyColorFilter +import com.simplemobiletools.commons.extensions.beGone +import com.simplemobiletools.commons.extensions.getDialogTheme +import com.simplemobiletools.commons.extensions.setupDialogStuff import kotlinx.android.synthetic.main.fragment_month.view.* import kotlinx.android.synthetic.main.top_navigation.view.* import org.joda.time.DateTime @@ -33,9 +31,9 @@ import org.joda.time.DateTime class MonthFragment : Fragment(), MonthlyCalendar { private var mTextColor = 0 private var mSundayFirst = false + private var mShowWeekNumbers = false private var mDayCode = "" private var mPackageName = "" - private var mDayLabelHeight = 0 private var mLastHash = 0L private var mCalendar: MonthlyCalendarImpl? = null @@ -52,10 +50,9 @@ class MonthFragment : Fragment(), MonthlyCalendar { mHolder = view.month_calendar_holder mDayCode = arguments!!.getString(DAY_CODE) mConfig = context!!.config - mSundayFirst = mConfig.isSundayFirst + storeStateVariables() setupButtons() - setupLabels() mCalendar = MonthlyCalendarImpl(this, context!!) return view @@ -63,14 +60,13 @@ class MonthFragment : Fragment(), MonthlyCalendar { override fun onPause() { super.onPause() - mSundayFirst = context!!.config.isSundayFirst + storeStateVariables() } override fun onResume() { super.onResume() - if (mConfig.isSundayFirst != mSundayFirst) { - mSundayFirst = mConfig.isSundayFirst - setupLabels() + if (mConfig.showWeekNumbers != mShowWeekNumbers) { + mLastHash = -1L } mCalendar!!.apply { @@ -78,14 +74,22 @@ class MonthFragment : Fragment(), MonthlyCalendar { getDays(false) // prefill the screen asap, even if without events } + storeStateVariables() updateCalendar() } + private fun storeStateVariables() { + mConfig.apply { + mSundayFirst = isSundayFirst + mShowWeekNumbers = showWeekNumbers + } + } + fun updateCalendar() { mCalendar?.updateMonthlyCalendar(Formatter.getDateTimeFromCode(mDayCode)) } - override fun updateMonthlyCalendar(context: Context, month: String, days: List, checkedEvents: Boolean) { + override fun updateMonthlyCalendar(context: Context, month: String, days: ArrayList, checkedEvents: Boolean) { val newHash = month.hashCode() + days.hashCode().toLong() if ((mLastHash != 0L && !checkedEvents) || mLastHash == newHash) { return @@ -153,51 +157,9 @@ class MonthFragment : Fragment(), MonthlyCalendar { listener?.goToDateTime(newDateTime) } - private fun setupLabels() { - val letters = context!!.resources.getStringArray(R.array.week_day_letters) - for (i in 0..6) { - var index = i - if (mSundayFirst) { - index = (index + 6) % letters.size - } - - mHolder.findViewById(mRes.getIdentifier("label_$i", "id", mPackageName)).apply { - setTextColor(mTextColor) - text = letters[index] - } - } - } - - private fun updateDays(days: List) { - val displayWeekNumbers = mConfig.displayWeekNumbers - val len = days.size - - if (week_num == null) - return - - week_num.setTextColor(mTextColor) - week_num.beVisibleIf(displayWeekNumbers) - - for (i in 0..5) { - mHolder.findViewById(mRes.getIdentifier("week_num_$i", "id", mPackageName)).apply { - text = "${days[i * 7 + 3].weekOfYear}:" // fourth day of the week matters - setTextColor(mTextColor) - beVisibleIf(displayWeekNumbers) - } - } - - val dividerMargin = mRes.displayMetrics.density.toInt() - for (i in 0 until len) { - mHolder.findViewById(mRes.getIdentifier("day_$i", "id", mPackageName)).apply { - val day = days[i] - setOnClickListener { - (activity as MainActivity).openDayFromMonthly(Formatter.getDateTimeFromCode(day.code)) - } - - removeAllViews() - context.addDayNumber(mTextColor, day, this, mDayLabelHeight) { mDayLabelHeight = it } - context.addDayEvents(day, this, mRes, dividerMargin) - } + private fun updateDays(days: ArrayList) { + mHolder.month_view_wrapper.updateDays(days) { + (activity as MainActivity).openDayFromMonthly(Formatter.getDateTimeFromCode(it.code)) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragmentsHolder.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragmentsHolder.kt index a22bfe593..7f71acd7e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragmentsHolder.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/MonthFragmentsHolder.kt @@ -72,7 +72,7 @@ class MonthFragmentsHolder : MyFragmentHolder(), NavigationListener { private fun getMonths(code: String): List { val months = ArrayList(PREFILLED_MONTHS) - val today = Formatter.getDateTimeFromCode(code) + val today = Formatter.getDateTimeFromCode(code).withDayOfMonth(1) for (i in -PREFILLED_MONTHS / 2..PREFILLED_MONTHS / 2) { months.add(Formatter.getDayCodeFromDateTime(today.plusMonths(i))) } @@ -108,5 +108,5 @@ class MonthFragmentsHolder : MyFragmentHolder(), NavigationListener { (activity as MainActivity).supportActionBar?.title = getString(R.string.app_launcher_name) } - override fun getNewEventDayCode() = currentDayCode + override fun getNewEventDayCode() = if (shouldGoToTodayBeVisible()) currentDayCode else todayDayCode } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/WeekFragment.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/WeekFragment.kt index 2cd5070ab..661e9656f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/WeekFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/fragments/WeekFragment.kt @@ -143,7 +143,9 @@ class WeekFragment : Fragment(), WeeklyCalendar { val todayCode = Formatter.getDayCodeFromDateTime(DateTime()) for (i in 0..6) { val dayCode = Formatter.getDayCodeFromDateTime(curDay) - val dayLetter = getDayLetter(curDay.dayOfWeek) + val dayLetters = mRes.getStringArray(R.array.week_day_letters).toList() as ArrayList + val dayLetter = dayLetters[curDay.dayOfWeek - 1] + mView.findViewById(mRes.getIdentifier("week_day_label_$i", "id", context!!.packageName)).apply { text = "$dayLetter\n${curDay.dayOfMonth}" setTextColor(if (todayCode == dayCode) primaryColor else textColor) @@ -155,18 +157,6 @@ class WeekFragment : Fragment(), WeeklyCalendar { } } - private fun getDayLetter(pos: Int): String { - return mRes.getString(when (pos) { - 1 -> R.string.monday_letter - 2 -> R.string.tuesday_letter - 3 -> R.string.wednesday_letter - 4 -> R.string.thursday_letter - 5 -> R.string.friday_letter - 6 -> R.string.saturday_letter - else -> R.string.sunday_letter - }) - } - private fun checkScrollLimits(y: Int) { if (minScrollY != -1 && y < minScrollY) { mScrollView.scrollY = minScrollY @@ -359,10 +349,10 @@ class WeekFragment : Fragment(), WeeklyCalendar { val minTS = Math.max(startDateTime.seconds(), mWeekTimestamp) val maxTS = Math.min(endDateTime.seconds(), mWeekTimestamp + WEEK_SECONDS) - val startDateTimeInWeek = Formatter.getDateTimeFromTS(minTS) - val firstDayIndex = (startDateTimeInWeek.dayOfWeek - if (context!!.config.isSundayFirst) 0 else 1) % 7 val daysCnt = Days.daysBetween(Formatter.getDateTimeFromTS(minTS).toLocalDate(), Formatter.getDateTimeFromTS(maxTS).toLocalDate()).days + val startDateTimeInWeek = Formatter.getDateTimeFromTS(minTS) + val firstDayIndex = (startDateTimeInWeek.dayOfWeek - if (context!!.config.isSundayFirst) 0 else 1) % 7 var doesEventFit: Boolean val cnt = allDayRows.size - 1 var wasEventHandled = false @@ -383,7 +373,7 @@ class WeekFragment : Fragment(), WeeklyCalendar { wasEventHandled = true } else if (index == cnt) { if (allDayRows.size == index + 1) { - allDayRows.add(HashSet()) + allDayRows.add(HashSet()) addNewLine() drawAtLine++ wasEventHandled = true diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/CalDAVHandler.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/CalDAVHandler.kt index b699e4d36..b812d5b26 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/CalDAVHandler.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/CalDAVHandler.kt @@ -361,6 +361,7 @@ class CalDAVHandler(val context: Context) { put(CalendarContract.Events.RRULE, Parser().getRepeatCode(event)) put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().toString()) put(CalendarContract.Events.EVENT_LOCATION, event.location) + put(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CONFIRMED) if (event.getIsAllDay() && event.endTS > event.startTS) event.endTS += DAY diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Config.kt index 919659d9f..8fd699de6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Config.kt @@ -1,9 +1,11 @@ package com.simplemobiletools.calendar.helpers import android.content.Context -import android.media.RingtoneManager import com.simplemobiletools.calendar.R import com.simplemobiletools.calendar.extensions.scheduleCalDAVSync +import com.simplemobiletools.commons.extensions.getDefaultAlarmTitle +import com.simplemobiletools.commons.extensions.getDefaultAlarmUri +import com.simplemobiletools.commons.helpers.ALARM_SOUND_TYPE_NOTIFICATION import com.simplemobiletools.commons.helpers.BaseConfig import java.util.* @@ -12,9 +14,9 @@ class Config(context: Context) : BaseConfig(context) { fun newInstance(context: Context) = Config(context) } - var displayWeekNumbers: Boolean + var showWeekNumbers: Boolean get() = prefs.getBoolean(WEEK_NUMBERS, false) - set(displayWeekNumbers) = prefs.edit().putBoolean(WEEK_NUMBERS, displayWeekNumbers).apply() + set(showWeekNumbers) = prefs.edit().putBoolean(WEEK_NUMBERS, showWeekNumbers).apply() var startWeeklyAt: Int get() = prefs.getInt(START_WEEKLY_AT, 7) @@ -28,9 +30,13 @@ class Config(context: Context) : BaseConfig(context) { get() = prefs.getBoolean(VIBRATE, false) set(vibrate) = prefs.edit().putBoolean(VIBRATE, vibrate).apply() - var reminderSound: String - get() = prefs.getString(REMINDER_SOUND, getDefaultNotificationSound()) - set(path) = prefs.edit().putString(REMINDER_SOUND, path).apply() + var reminderSoundUri: String + get() = prefs.getString(REMINDER_SOUND_URI, context.getDefaultAlarmUri(ALARM_SOUND_TYPE_NOTIFICATION).toString()) + set(reminderSoundUri) = prefs.edit().putString(REMINDER_SOUND_URI, reminderSoundUri).apply() + + var reminderSoundTitle: String + get() = prefs.getString(REMINDER_SOUND_TITLE, context.getDefaultAlarmTitle(ALARM_SOUND_TYPE_NOTIFICATION)) + set(reminderSoundTitle) = prefs.edit().putString(REMINDER_SOUND_TITLE, reminderSoundTitle).apply() var storedView: Int get() = prefs.getInt(VIEW, MONTHLY_VIEW) @@ -79,6 +85,10 @@ class Config(context: Context) : BaseConfig(context) { get() = prefs.getBoolean(REPLACE_DESCRIPTION, false) set(replaceDescription) = prefs.edit().putBoolean(REPLACE_DESCRIPTION, replaceDescription).apply() + var showGrid: Boolean + get() = prefs.getBoolean(SHOW_GRID, false) + set(showGrid) = prefs.edit().putBoolean(SHOW_GRID, showGrid).apply() + fun getSyncedCalendarIdsAsList() = caldavSyncedCalendarIDs.split(",").filter { it.trim().isNotEmpty() } as ArrayList fun addDisplayEventType(type: String) { @@ -97,14 +107,6 @@ class Config(context: Context) : BaseConfig(context) { displayEventTypes = currDisplayEventTypes } - private fun getDefaultNotificationSound(): String { - return try { - RingtoneManager.getActualDefaultRingtoneUri(context, RingtoneManager.TYPE_NOTIFICATION)?.toString() ?: "" - } catch (e: Exception) { - "" - } - } - fun getFontSize() = when (fontSize) { FONT_SIZE_SMALL -> getSmallFontSize() FONT_SIZE_MEDIUM -> getMediumFontSize() diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Constants.kt index 844d6f4cc..ab8956029 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Constants.kt @@ -37,7 +37,8 @@ const val WEEK_NUMBERS = "week_numbers" const val START_WEEKLY_AT = "start_weekly_at" const val END_WEEKLY_AT = "end_weekly_at" const val VIBRATE = "vibrate" -const val REMINDER_SOUND = "reminder_sound" +const val REMINDER_SOUND_URI = "reminder_sound_uri" +const val REMINDER_SOUND_TITLE = "reminder_sound_title" const val VIEW = "view" const val REMINDER_MINUTES = "reminder_minutes" const val REMINDER_MINUTES_2 = "reminder_minutes_2" @@ -49,6 +50,7 @@ const val CALDAV_SYNCED_CALENDAR_IDS = "caldav_synced_calendar_ids" const val LAST_USED_CALDAV_CALENDAR = "last_used_caldav_calendar" const val DISPLAY_PAST_EVENTS = "display_past_events" const val REPLACE_DESCRIPTION = "replace_description" +const val SHOW_GRID = "show_grid" // repeat_rule for monthly repetition const val REPEAT_MONTH_SAME_DAY = 1 // ie 25th every month diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/DBHelper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/DBHelper.kt index 99ae0a221..acff07eac 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/DBHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/DBHelper.kt @@ -225,6 +225,35 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont callback(event.id) } + fun insertEvents(events: ArrayList, addToCalDAV: Boolean) { + mDb.beginTransaction() + try { + for (event in events) { + if (event.startTS > event.endTS) { + continue + } + + val eventValues = fillEventValues(event) + val id = mDb.insert(MAIN_TABLE_NAME, null, eventValues) + event.id = id.toInt() + + if (event.repeatInterval != 0 && event.parentId == 0) { + val metaValues = fillMetaValues(event) + mDb.insert(META_TABLE_NAME, null, metaValues) + } + + context.scheduleNextEventReminder(event, this) + if (addToCalDAV && event.source != SOURCE_SIMPLE_CALENDAR && context.config.caldavSync) { + CalDAVHandler(context).insertCalDAVEvent(event) + } + } + mDb.setTransactionSuccessful() + } finally { + mDb.endTransaction() + context.updateWidgets() + } + } + fun update(event: Event, updateAtCalDAV: Boolean, callback: (() -> Unit)? = null) { val values = fillEventValues(event) val selection = "$COL_ID = ?" @@ -605,6 +634,18 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont } } + fun getEventIdWithLastImportId(id: String): Int { + val selection = "$MAIN_TABLE_NAME.$COL_IMPORT_ID LIKE ?" + val selectionArgs = arrayOf("%-$id") + val cursor = getEventsCursor(selection, selectionArgs) + val events = fillEvents(cursor) + return if (events.isNotEmpty()) { + events.minBy { it.id }?.id ?: 0 + } else { + 0 + } + } + fun getEventsWithSearchQuery(text: String, callback: (searchedText: String, events: List) -> Unit) { Thread { val searchQuery = "%$text%" diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Formatter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Formatter.kt index 1605e164a..0bfe927db 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Formatter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/Formatter.kt @@ -9,7 +9,6 @@ import org.joda.time.DateTime import org.joda.time.DateTimeZone import org.joda.time.LocalDate import org.joda.time.format.DateTimeFormat -import java.text.DateFormat object Formatter { val DAYCODE_PATTERN = "YYYYMMdd" @@ -89,9 +88,9 @@ object Formatter { fun getYear(dateTime: DateTime) = dateTime.toString(YEAR_PATTERN) - fun getHourPattern(context: Context) = if (context.config.use24hourFormat) PATTERN_HOURS_24 else PATTERN_HOURS_12 + fun getHourPattern(context: Context) = if (context.config.use24HourFormat) PATTERN_HOURS_24 else PATTERN_HOURS_12 - fun getTimePattern(context: Context) = if (context.config.use24hourFormat) PATTERN_TIME_24 else PATTERN_TIME_12 + fun getTimePattern(context: Context) = if (context.config.use24HourFormat) PATTERN_TIME_24 else PATTERN_TIME_12 fun getExportedTime(ts: Long): String { val dateTime = DateTime(ts, DateTimeZone.UTC) @@ -108,6 +107,4 @@ object Formatter { } fun getShiftedImportTimestamp(ts: Int) = getUTCDateTimeFromTS(ts).withTime(13, 0, 0, 0).withZoneRetainFields(DateTimeZone.getDefault()).seconds() - - fun getDayAndMonth(localDate: LocalDate) = localDate.toString("dd.MM.") } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsExporter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsExporter.kt index b5f72072b..472c967f6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsExporter.kt @@ -1,10 +1,12 @@ package com.simplemobiletools.calendar.helpers +import com.simplemobiletools.calendar.R import com.simplemobiletools.calendar.extensions.dbHelper import com.simplemobiletools.calendar.helpers.IcsExporter.ExportResult.* import com.simplemobiletools.calendar.models.Event import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.extensions.getFileOutputStream +import com.simplemobiletools.commons.extensions.toast import com.simplemobiletools.commons.extensions.writeLn import com.simplemobiletools.commons.models.FileDirItem import java.io.BufferedWriter @@ -18,14 +20,18 @@ class IcsExporter { private var eventsExported = 0 private var eventsFailed = 0 - fun exportEvents(activity: BaseSimpleActivity, file: File, events: ArrayList, callback: (result: ExportResult) -> Unit) { + fun exportEvents(activity: BaseSimpleActivity, file: File, events: ArrayList, showExportingToast: Boolean, callback: (result: ExportResult) -> Unit) { val fileDirItem = FileDirItem(file.absolutePath, file.name) - activity.getFileOutputStream(fileDirItem) { + activity.getFileOutputStream(fileDirItem, true) { if (it == null) { callback(EXPORT_FAIL) return@getFileOutputStream } + if (showExportingToast) { + activity.toast(R.string.exporting) + } + it.bufferedWriter().use { out -> out.writeLn(BEGIN_CALENDAR) out.writeLn(CALENDAR_PRODID) diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsImporter.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsImporter.kt index 947254241..eff91f071 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsImporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/IcsImporter.kt @@ -43,6 +43,7 @@ class IcsImporter(val activity: SimpleActivity) { fun importEvents(path: String, defaultEventType: Int, calDAVCalendarId: Int): ImportResult { try { val existingEvents = activity.dbHelper.getEventsWithImportIds() + val eventsToInsert = ArrayList() var prevLine = "" val inputStream = if (path.contains("/")) { @@ -131,7 +132,6 @@ class IcsImporter(val activity: SimpleActivity) { continue } - val eventToUpdate = existingEvents.firstOrNull { curImportId.isNotEmpty() && curImportId == it.importId } if (eventToUpdate != null && eventToUpdate.lastUpdated >= curLastModified) { continue @@ -148,11 +148,15 @@ class IcsImporter(val activity: SimpleActivity) { } if (eventToUpdate == null) { - activity.dbHelper.insert(event, true) { - for (exceptionTS in curRepeatExceptions) { - activity.dbHelper.addEventRepeatException(it, exceptionTS, true) + if (curRepeatExceptions.isEmpty()) { + eventsToInsert.add(event) + } else { + activity.dbHelper.insert(event, true) { + for (exceptionTS in curRepeatExceptions) { + activity.dbHelper.addEventRepeatException(it, exceptionTS, true) + } + existingEvents.add(event) } - existingEvents.add(event) } } else { event.id = eventToUpdate.id @@ -164,6 +168,8 @@ class IcsImporter(val activity: SimpleActivity) { prevLine = line } } + + activity.dbHelper.insertEvents(eventsToInsert, true) } catch (e: Exception) { activity.showErrorToast(e, Toast.LENGTH_LONG) eventsFailed++ diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MonthlyCalendarImpl.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MonthlyCalendarImpl.kt index 5e43361e7..2c6fa6b5d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MonthlyCalendarImpl.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MonthlyCalendarImpl.kt @@ -25,8 +25,8 @@ class MonthlyCalendarImpl(val mCallback: MonthlyCalendar, val mContext: Context) fun updateMonthlyCalendar(targetDate: DateTime, filterEventTypes: Boolean = true) { mFilterEventTypes = filterEventTypes mTargetDate = targetDate - val startTS = mTargetDate.minusMonths(1).seconds() - val endTS = mTargetDate.plusMonths(1).seconds() + val startTS = mTargetDate.minusDays(7).seconds() + val endTS = mTargetDate.plusDays(43).seconds() mContext.dbHelper.getEvents(startTS, endTS) { gotEvents(it as ArrayList) } @@ -47,7 +47,7 @@ class MonthlyCalendarImpl(val mCallback: MonthlyCalendar, val mContext: Context) var isThisMonth = false var isToday: Boolean var value = prevMonthDays - firstDayIndex + 1 - var curDay: DateTime = mTargetDate + var curDay = mTargetDate for (i in 0 until DAYS_CNT) { when { @@ -67,11 +67,11 @@ class MonthlyCalendarImpl(val mCallback: MonthlyCalendar, val mContext: Context) } } - isToday = isThisMonth && isToday(mTargetDate, value) + isToday = isToday(curDay, value) val newDay = curDay.withDayOfMonth(value) val dayCode = Formatter.getDayCodeFromDateTime(newDay) - val day = DayMonthly(value, isThisMonth, isToday, dayCode, newDay.weekOfWeekyear, ArrayList()) + val day = DayMonthly(value, isThisMonth, isToday, dayCode, newDay.weekOfWeekyear, ArrayList(), i) days.add(day) value++ } @@ -94,14 +94,16 @@ class MonthlyCalendarImpl(val mCallback: MonthlyCalendar, val mContext: Context) var currDay = startDateTime var dayCode = Formatter.getDayCodeFromDateTime(currDay) - var currDayEvents = (dayEvents[dayCode] ?: ArrayList()).apply { add(it) } - dayEvents.put(dayCode, currDayEvents) + var currDayEvents = dayEvents[dayCode] ?: ArrayList() + currDayEvents.add(it) + dayEvents[dayCode] = currDayEvents while (Formatter.getDayCodeFromDateTime(currDay) != endCode) { currDay = currDay.plusDays(1) dayCode = Formatter.getDayCodeFromDateTime(currDay) - currDayEvents = (dayEvents[dayCode] ?: ArrayList()).apply { add(it) } - dayEvents.put(dayCode, currDayEvents) + currDayEvents = dayEvents[dayCode] ?: ArrayList() + currDayEvents.add(it) + dayEvents[dayCode] = currDayEvents } } @@ -113,10 +115,8 @@ class MonthlyCalendarImpl(val mCallback: MonthlyCalendar, val mContext: Context) } private fun isToday(targetDate: DateTime, curDayInMonth: Int): Boolean { - return if (curDayInMonth > targetDate.dayOfMonth().maximumValue) - false - else - targetDate.withDayOfMonth(curDayInMonth).toString(Formatter.DAYCODE_PATTERN) == mToday + val targetMonthDays = targetDate.dayOfMonth().maximumValue + return targetDate.withDayOfMonth(Math.min(curDayInMonth, targetMonthDays)).toString(Formatter.DAYCODE_PATTERN) == mToday } private val monthName: String diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MyWidgetMonthlyProvider.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MyWidgetMonthlyProvider.kt index 7ebff7fdd..cf707ea24 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MyWidgetMonthlyProvider.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/helpers/MyWidgetMonthlyProvider.kt @@ -24,7 +24,7 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { private val NEW_EVENT = "new_event" companion object { - private var targetDate = DateTime.now() + private var targetDate = DateTime.now().withDayOfMonth(1) } override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { @@ -82,7 +82,7 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { } private fun updateDays(context: Context, views: RemoteViews, days: List) { - val displayWeekNumbers = context.config.displayWeekNumbers + val displayWeekNumbers = context.config.showWeekNumbers val textColor = context.config.widgetTextColor val smallerFontSize = context.config.getFontSize() - 3f val res = context.resources @@ -149,7 +149,7 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { } private val monthlyCalendar = object : MonthlyCalendar { - override fun updateMonthlyCalendar(context: Context, month: String, days: List, checkedEvents: Boolean) { + override fun updateMonthlyCalendar(context: Context, month: String, days: ArrayList, checkedEvents: Boolean) { val largerFontSize = context.config.getFontSize() + 3f val textColor = context.config.widgetTextColor val resources = context.resources @@ -182,7 +182,10 @@ class MyWidgetMonthlyProvider : AppWidgetProvider() { val monthCode = days.firstOrNull { it.code.substring(6) == "01" }?.code ?: Formatter.getTodayCode(context) setupAppOpenIntent(context, views, R.id.top_value, monthCode) - appWidgetManager.updateAppWidget(it, views) + try { + appWidgetManager.updateAppWidget(it, views) + } catch (ignored: RuntimeException) { + } } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/interfaces/MonthlyCalendar.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/interfaces/MonthlyCalendar.kt index 1f7c188b5..1ada47f89 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/interfaces/MonthlyCalendar.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/interfaces/MonthlyCalendar.kt @@ -4,5 +4,5 @@ import android.content.Context import com.simplemobiletools.calendar.models.DayMonthly interface MonthlyCalendar { - fun updateMonthlyCalendar(context: Context, month: String, days: List, checkedEvents: Boolean) + fun updateMonthlyCalendar(context: Context, month: String, days: ArrayList, checkedEvents: Boolean) } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/models/DayMonthly.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/models/DayMonthly.kt index 8f8cfe30e..55e9d57a4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/models/DayMonthly.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/models/DayMonthly.kt @@ -1,6 +1,7 @@ package com.simplemobiletools.calendar.models -data class DayMonthly(val value: Int, val isThisMonth: Boolean, val isToday: Boolean, val code: String, val weekOfYear: Int, var dayEvents: ArrayList) { +data class DayMonthly(val value: Int, val isThisMonth: Boolean, val isToday: Boolean, val code: String, val weekOfYear: Int, var dayEvents: ArrayList, + var indexOnMonthView: Int) { fun hasEvent() = dayEvents.isNotEmpty() } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/models/MonthViewEvent.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/models/MonthViewEvent.kt new file mode 100644 index 000000000..e68b819b3 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/models/MonthViewEvent.kt @@ -0,0 +1,4 @@ +package com.simplemobiletools.calendar.models + +data class MonthViewEvent(val id: Int, val title: String, val startTS: Int, val color: Int, val startDayIndex: Int, val daysCnt: Int, val originalStartDayIndex: Int, + val isAllDay: Boolean) diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/BootCompletedReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/BootCompletedReceiver.kt index 96c163240..d6aec0c36 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/BootCompletedReceiver.kt @@ -9,11 +9,13 @@ import com.simplemobiletools.calendar.extensions.scheduleAllEvents class BootCompletedReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, arg1: Intent) { - context.apply { - scheduleAllEvents() - notifyRunningEvents() - recheckCalDAVCalendars {} - } + override fun onReceive(context: Context, intent: Intent) { + Thread { + context.apply { + scheduleAllEvents() + notifyRunningEvents() + recheckCalDAVCalendars {} + } + }.start() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/NotificationReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/NotificationReceiver.kt index 80aa2c733..3fb716f84 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/NotificationReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/receivers/NotificationReceiver.kt @@ -15,14 +15,20 @@ class NotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager val wakelock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Simple Calendar") - wakelock.acquire(5000) + wakelock.acquire(3000) - context.updateListWidget() + Thread { + handleIntent(context, intent) + }.start() + } + + private fun handleIntent(context: Context, intent: Intent) { val id = intent.getIntExtra(EVENT_ID, -1) if (id == -1) { return } + context.updateListWidget() val event = context.dbHelper.getEventWithId(id) if (event == null || event.getReminders().isEmpty()) { return diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/views/MonthView.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/views/MonthView.kt new file mode 100644 index 000000000..9b0ef9579 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/views/MonthView.kt @@ -0,0 +1,335 @@ +package com.simplemobiletools.calendar.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import android.text.TextPaint +import android.text.TextUtils +import android.util.AttributeSet +import android.util.SparseIntArray +import android.view.View +import com.simplemobiletools.calendar.R +import com.simplemobiletools.calendar.extensions.config +import com.simplemobiletools.calendar.extensions.seconds +import com.simplemobiletools.calendar.helpers.Formatter +import com.simplemobiletools.calendar.helpers.LOW_ALPHA +import com.simplemobiletools.calendar.models.DayMonthly +import com.simplemobiletools.calendar.models.Event +import com.simplemobiletools.calendar.models.MonthViewEvent +import com.simplemobiletools.commons.extensions.adjustAlpha +import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor +import com.simplemobiletools.commons.extensions.getContrastColor +import com.simplemobiletools.commons.extensions.moveLastItemToFront +import org.joda.time.DateTime +import org.joda.time.Days + +// used in the Monthly view fragment, 1 view per screen +class MonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(context, attrs, defStyle) { + private val BG_CORNER_RADIUS = 4f + private val ROW_COUNT = 6 + + private var paint: Paint + private var eventTitlePaint: TextPaint + private var gridPaint: Paint + private var dayWidth = 0f + private var dayHeight = 0f + private var primaryColor = 0 + private var textColor = 0 + private var weekDaysLetterHeight = 0 + private var eventTitleHeight = 0 + private var currDayOfWeek = 0 + private var smallPadding = 0 + private var maxEventsPerDay = 0 + private var horizontalOffset = 0 + private var showWeekNumbers = false + private var allEvents = ArrayList() + private var bgRectF = RectF() + private var dayLetters = ArrayList() + private var days = ArrayList() + private var dayVerticalOffsets = SparseIntArray() + + constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) + + init { + primaryColor = context.getAdjustedPrimaryColor() + textColor = context.config.textColor + showWeekNumbers = context.config.showWeekNumbers + + smallPadding = resources.displayMetrics.density.toInt() + val normalTextSize = resources.getDimensionPixelSize(R.dimen.normal_text_size) + weekDaysLetterHeight = normalTextSize * 2 + + paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = textColor + textSize = normalTextSize.toFloat() + textAlign = Paint.Align.CENTER + } + + gridPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = textColor.adjustAlpha(LOW_ALPHA) + } + + val smallerTextSize = resources.getDimensionPixelSize(R.dimen.smaller_text_size) + eventTitleHeight = smallerTextSize + eventTitlePaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply { + color = textColor + textSize = smallerTextSize.toFloat() + textAlign = Paint.Align.LEFT + } + + initWeekDayLetters() + setupCurrentDayOfWeekIndex() + } + + fun updateDays(newDays: ArrayList) { + days = newDays + showWeekNumbers = context.config.showWeekNumbers + horizontalOffset = if (showWeekNumbers) eventTitleHeight * 2 else 0 + initWeekDayLetters() + setupCurrentDayOfWeekIndex() + groupAllEvents() + invalidate() + } + + private fun groupAllEvents() { + days.forEach { + val day = it + day.dayEvents.forEach { + val event = it + + // make sure we properly handle events lasting multiple days and repeating ones + val lastEvent = allEvents.lastOrNull { it.id == event.id } + val daysCnt = getEventLastingDaysCount(event) + if (lastEvent == null || lastEvent.startDayIndex + daysCnt <= day.indexOnMonthView) { + val monthViewEvent = MonthViewEvent(event.id, event.title, event.startTS, event.color, day.indexOnMonthView, + daysCnt, day.indexOnMonthView, event.getIsAllDay()) + allEvents.add(monthViewEvent) + } + } + } + + allEvents = allEvents.sortedWith(compareBy({ -it.daysCnt }, { !it.isAllDay }, { it.startTS }, { it.startDayIndex }, { it.title })).toMutableList() as ArrayList + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + dayVerticalOffsets.clear() + measureDaySize(canvas) + + if (context.config.showGrid) { + drawGrid(canvas) + } + + addWeekDayLetters(canvas) + if (showWeekNumbers) { + addWeekNumbers(canvas) + } + + var curId = 0 + for (y in 0 until ROW_COUNT) { + for (x in 0..6) { + val day = days.getOrNull(curId) + if (day != null) { + dayVerticalOffsets.put(day.indexOnMonthView, dayVerticalOffsets[day.indexOnMonthView] + weekDaysLetterHeight) + val verticalOffset = dayVerticalOffsets[day.indexOnMonthView] + val xPos = x * dayWidth + horizontalOffset + val yPos = y * dayHeight + verticalOffset + val xPosCenter = xPos + dayWidth / 2 + if (day.isToday) { + canvas.drawCircle(xPosCenter, yPos + paint.textSize * 0.7f, paint.textSize * 0.75f, getCirclePaint(day)) + } + + canvas.drawText(day.value.toString(), xPosCenter, yPos + paint.textSize, getTextPaint(day)) + dayVerticalOffsets.put(day.indexOnMonthView, (verticalOffset + paint.textSize * 2).toInt()) + } + curId++ + } + } + + drawEvents(canvas) + } + + private fun drawGrid(canvas: Canvas) { + // vertical lines + for (i in 0..6) { + var lineX = i * dayWidth + if (showWeekNumbers) { + lineX += horizontalOffset + } + canvas.drawLine(lineX, 0f, lineX, canvas.height.toFloat(), gridPaint) + } + + // horizontal lines + canvas.drawLine(0f, 0f, canvas.width.toFloat(), 0f, gridPaint) + for (i in 0..5) { + canvas.drawLine(0f, i * dayHeight + weekDaysLetterHeight, canvas.width.toFloat(), i * dayHeight + weekDaysLetterHeight, gridPaint) + } + } + + private fun addWeekDayLetters(canvas: Canvas) { + for (i in 0..6) { + val xPos = horizontalOffset + (i + 1) * dayWidth - dayWidth / 2 + var weekDayLetterPaint = paint + if (i == currDayOfWeek) { + weekDayLetterPaint = getColoredPaint(primaryColor) + } + canvas.drawText(dayLetters[i], xPos, weekDaysLetterHeight * 0.7f, weekDayLetterPaint) + } + } + + private fun addWeekNumbers(canvas: Canvas) { + val weekNumberPaint = Paint(paint) + weekNumberPaint.textAlign = Paint.Align.RIGHT + + for (i in 0 until ROW_COUNT) { + // fourth day of the week matters + val weekOfYear = days.getOrNull(i * 7 + 3)?.weekOfYear ?: 1 + val id = "$weekOfYear:" + + val yPos = i * dayHeight + weekDaysLetterHeight + canvas.drawText(id, horizontalOffset.toFloat() * 0.9f, yPos + paint.textSize, weekNumberPaint) + } + } + + private fun measureDaySize(canvas: Canvas) { + dayWidth = (canvas.width - horizontalOffset) / 7f + dayHeight = (canvas.height - weekDaysLetterHeight) / ROW_COUNT.toFloat() + val availableHeightForEvents = dayHeight.toInt() - weekDaysLetterHeight + maxEventsPerDay = availableHeightForEvents / eventTitleHeight + } + + private fun drawEvents(canvas: Canvas) { + for (event in allEvents) { + drawEvent(event, canvas) + } + } + + private fun drawEvent(event: MonthViewEvent, canvas: Canvas) { + val verticalOffset = dayVerticalOffsets[event.startDayIndex] + val xPos = event.startDayIndex % 7 * dayWidth + horizontalOffset + val yPos = (event.startDayIndex / 7) * dayHeight + val xPosCenter = xPos + dayWidth / 2 + + if (verticalOffset - eventTitleHeight * 2 > dayHeight) { + canvas.drawText("...", xPosCenter, yPos + verticalOffset - eventTitleHeight / 2, getTextPaint(days[event.startDayIndex])) + return + } + + // event background rectangle + val backgroundY = yPos + verticalOffset + val bgLeft = xPos + smallPadding + val bgTop = backgroundY + smallPadding - eventTitleHeight + var bgRight = xPos - smallPadding + dayWidth * event.daysCnt + val bgBottom = backgroundY + smallPadding * 2 + if (bgRight > canvas.width.toFloat()) { + bgRight = canvas.width.toFloat() - smallPadding + val newStartDayIndex = (event.startDayIndex / 7 + 1) * 7 + if (newStartDayIndex < 42) { + val newEvent = event.copy(startDayIndex = newStartDayIndex, daysCnt = event.daysCnt - (newStartDayIndex - event.startDayIndex)) + drawEvent(newEvent, canvas) + } + } + + val startDayIndex = days[event.originalStartDayIndex] + val endDayIndex = days[Math.min(event.startDayIndex + event.daysCnt - 1, 41)] + bgRectF.set(bgLeft, bgTop, bgRight, bgBottom) + canvas.drawRoundRect(bgRectF, BG_CORNER_RADIUS, BG_CORNER_RADIUS, getEventBackgroundColor(event, startDayIndex, endDayIndex)) + + drawEventTitle(event.title, canvas, xPos, yPos + verticalOffset, bgRight - bgLeft - smallPadding, event.color, startDayIndex, endDayIndex) + + for (i in 0 until Math.min(event.daysCnt, 7 - event.startDayIndex % 7)) { + dayVerticalOffsets.put(event.startDayIndex + i, verticalOffset + eventTitleHeight + smallPadding * 2) + } + } + + private fun drawEventTitle(title: String, canvas: Canvas, x: Float, y: Float, availableWidth: Float, eventColor: Int, startDay: DayMonthly, endDay: DayMonthly) { + val ellipsized = TextUtils.ellipsize(title, eventTitlePaint, availableWidth - smallPadding, TextUtils.TruncateAt.END) + canvas.drawText(title, 0, ellipsized.length, x + smallPadding * 2, y, getEventTitlePaint(eventColor, startDay, endDay)) + } + + private fun getTextPaint(startDay: DayMonthly): Paint { + var paintColor = textColor + if (startDay.isToday) { + paintColor = primaryColor.getContrastColor() + } + + if (!startDay.isThisMonth) { + paintColor = paintColor.adjustAlpha(LOW_ALPHA) + } + + return getColoredPaint(paintColor) + } + + private fun getColoredPaint(color: Int): Paint { + val curPaint = Paint(paint) + curPaint.color = color + return curPaint + } + + private fun getEventBackgroundColor(event: MonthViewEvent, startDay: DayMonthly, endDay: DayMonthly): Paint { + var paintColor = event.color + if (!startDay.isThisMonth && !endDay.isThisMonth) { + paintColor = paintColor.adjustAlpha(LOW_ALPHA) + } + + return getColoredPaint(paintColor) + } + + private fun getEventTitlePaint(color: Int, startDay: DayMonthly, endDay: DayMonthly): Paint { + var paintColor = color.getContrastColor() + if (!startDay.isThisMonth && !endDay.isThisMonth) { + paintColor = paintColor.adjustAlpha(LOW_ALPHA) + } + + val curPaint = Paint(eventTitlePaint) + curPaint.color = paintColor + return curPaint + } + + private fun getCirclePaint(day: DayMonthly): Paint { + val curPaint = Paint(paint) + var paintColor = primaryColor + if (!day.isThisMonth) { + paintColor = paintColor.adjustAlpha(LOW_ALPHA) + } + curPaint.color = paintColor + return curPaint + } + + private fun initWeekDayLetters() { + dayLetters = context.resources.getStringArray(R.array.week_day_letters).toList() as ArrayList + if (context.config.isSundayFirst) { + dayLetters.moveLastItemToFront() + } + } + + private fun setupCurrentDayOfWeekIndex() { + if (days.firstOrNull { it.isToday && it.isThisMonth } == null) { + currDayOfWeek = -1 + return + } + + currDayOfWeek = DateTime().dayOfWeek + if (context.config.isSundayFirst) { + currDayOfWeek %= 7 + } else { + currDayOfWeek-- + } + } + + // take into account cases when an event starts on the previous screen, subtract those days + private fun getEventLastingDaysCount(event: Event): Int { + val startDateTime = Formatter.getDateTimeFromTS(event.startTS) + val endDateTime = Formatter.getDateTimeFromTS(event.endTS) + val code = days.first().code + val screenStartDateTime = Formatter.getDateTimeFromCode(code).toLocalDate() + var eventStartDateTime = Formatter.getDateTimeFromTS(startDateTime.seconds()).toLocalDate() + val eventEndDateTime = Formatter.getDateTimeFromTS(endDateTime.seconds()).toLocalDate() + val diff = Days.daysBetween(screenStartDateTime, eventStartDateTime).days + if (diff < 0) { + eventStartDateTime = screenStartDateTime + } + return Days.daysBetween(eventStartDateTime, eventEndDateTime).days + 1 + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/views/MonthViewWrapper.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/views/MonthViewWrapper.kt new file mode 100644 index 000000000..0a3ff142d --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/views/MonthViewWrapper.kt @@ -0,0 +1,99 @@ +package com.simplemobiletools.calendar.views + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import com.simplemobiletools.calendar.R +import com.simplemobiletools.calendar.extensions.config +import com.simplemobiletools.calendar.models.DayMonthly +import com.simplemobiletools.commons.extensions.onGlobalLayout +import kotlinx.android.synthetic.main.month_view.view.* + +// used in the Monthly view fragment, 1 view per screen +class MonthViewWrapper(context: Context, attrs: AttributeSet, defStyle: Int) : FrameLayout(context, attrs, defStyle) { + private var dayWidth = 0f + private var dayHeight = 0f + private var weekDaysLetterHeight = 0 + private var wereViewsAdded = false + private var days = ArrayList() + private var inflater: LayoutInflater + private var monthView: MonthView + private var horizontalOffset = 0 + private var dayClickCallback: ((day: DayMonthly) -> Unit)? = null + + constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) + + init { + val normalTextSize = resources.getDimensionPixelSize(R.dimen.normal_text_size).toFloat() + weekDaysLetterHeight = 2 * normalTextSize.toInt() + + inflater = LayoutInflater.from(context) + monthView = inflater.inflate(R.layout.month_view, this).month_view + setupHorizontalOffset() + + onGlobalLayout { + if (!wereViewsAdded && days.isNotEmpty()) { + measureSizes() + addViews() + monthView.updateDays(days) + } + } + } + + fun updateDays(newDays: ArrayList, callback: ((DayMonthly) -> Unit)? = null) { + setupHorizontalOffset() + measureSizes() + dayClickCallback = callback + days = newDays + if (dayWidth != 0f && dayHeight != 0f) { + addViews() + } + monthView.updateDays(days) + } + + private fun setupHorizontalOffset() { + horizontalOffset = if (context.config.showWeekNumbers) resources.getDimensionPixelSize(R.dimen.smaller_text_size) * 2 else 0 + } + + private fun measureSizes() { + dayWidth = (width - horizontalOffset) / 7f + + // avoid updating the height when coming back from a new event screen, when the keyboard was visible + val newHeight = (height - weekDaysLetterHeight) / 6f + if (newHeight > dayHeight) { + dayHeight = (height - weekDaysLetterHeight) / 6f + } + } + + private fun addViews() { + removeAllViews() + monthView = inflater.inflate(R.layout.month_view, this).month_view + wereViewsAdded = true + var curId = 0 + for (y in 0..5) { + for (x in 0..6) { + val day = days.getOrNull(curId) + if (day != null) { + val xPos = x * dayWidth + horizontalOffset + val yPos = y * dayHeight + weekDaysLetterHeight + addViewBackground(xPos, yPos, day) + } + curId++ + } + } + } + + private fun addViewBackground(xPos: Float, yPos: Float, day: DayMonthly) { + inflater.inflate(R.layout.month_view_background, this, false).apply { + layoutParams.width = dayWidth.toInt() + layoutParams.height = dayHeight.toInt() + x = xPos + y = yPos + setOnClickListener { + dayClickCallback?.invoke(day) + } + addView(this) + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/calendar/views/SmallMonthView.kt b/app/src/main/kotlin/com/simplemobiletools/calendar/views/SmallMonthView.kt index 9ba798b32..af53d3b8c 100644 --- a/app/src/main/kotlin/com/simplemobiletools/calendar/views/SmallMonthView.kt +++ b/app/src/main/kotlin/com/simplemobiletools/calendar/views/SmallMonthView.kt @@ -14,6 +14,7 @@ import com.simplemobiletools.commons.extensions.adjustAlpha import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor import java.util.* +// used for displaying months at Yearly view class SmallMonthView(context: Context, attrs: AttributeSet, defStyle: Int) : View(context, attrs, defStyle) { private var paint: Paint private var todayCirclePaint: Paint diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 6fb597e5d..6df46ade7 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -494,6 +494,29 @@ + + + + + + - - - + android:layout_below="@+id/top_left_arrow"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_month_widget_config.xml b/app/src/main/res/layout/fragment_month_widget_config.xml new file mode 100644 index 000000000..a7c627fdb --- /dev/null +++ b/app/src/main/res/layout/fragment_month_widget_config.xml @@ -0,0 +1,505 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/month_view.xml b/app/src/main/res/layout/month_view.xml new file mode 100644 index 000000000..0294b179d --- /dev/null +++ b/app/src/main/res/layout/month_view.xml @@ -0,0 +1,6 @@ + + diff --git a/app/src/main/res/layout/month_view_background.xml b/app/src/main/res/layout/month_view_background.xml new file mode 100644 index 000000000..f7eb272d5 --- /dev/null +++ b/app/src/main/res/layout/month_view_background.xml @@ -0,0 +1,7 @@ + + diff --git a/app/src/main/res/layout/widget_config_monthly.xml b/app/src/main/res/layout/widget_config_monthly.xml index f96a3f1d5..a62067ac6 100644 --- a/app/src/main/res/layout/widget_config_monthly.xml +++ b/app/src/main/res/layout/widget_config_monthly.xml @@ -9,7 +9,7 @@ Hadisə uğurla yeniləndi Hadisələri yazıya görə sırala Xahiş olunur məkan parametrini doldurun ki hadisədə görünsün + An event is upcoming Təkrarlama @@ -130,7 +131,7 @@ Bayram əlavə et Milli bayramlar Dini bayramlar - Bayramlar uğurla yeni hadisə tipinə daxil edildi + Holidays have been successfully imported into the \"Holidays\" event type Bəzi bayramları daxil etmək alınmadı Bayramları daxil etmək alınmadı @@ -150,6 +151,7 @@ Hadisə əlavəsini məkan ilə dəyiş Bütün hadisələri sil Bütün hadisələri silmək istədiyinizə əminsiniz? Bu hadisələri və parametrlərini bütövlüklə siləcək. + Show a grid CalDAV @@ -205,7 +207,7 @@ Kontakt icazəsi yalnız kontakt ad günlərini və il dönümlərini daxil etmək üçün istifadə olunur. - Bu, böyük bir tətbiq seriyasının yalnız bir hissəsidir. Digər tətbiqləri buradan əldə edə bilərsiniz http://www.simplemobiletools.com + Bu, böyük bir tətbiq seriyasının yalnız bir hissəsidir. Digər tətbiqləri buradan əldə edə bilərsiniz https://www.simplemobiletools.com Addegouezh @@ -130,7 +131,7 @@ Ouzhpennañ devezhioù gouel Devezhioù gouel broadel Devezhioù gouel relijiel - An devezhioù gouel a zo bet enporzhiet en un doare darvoud nevez gant berzh + Holidays have been successfully imported into the \"Holidays\" event type C\'hwitadenn en ur enporzhiañ ul lodenn eus an darvoudoù C\'hwitadenn en ur enporzhiañ ul lodenn eus an devezhioù gouel @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ Ret eo reiñ an aotre Darempredoù evit enporzhiañ deizioù-ha-bloaz an darempredoù. - An arload-mañ a zo ul lodenn eus un heuliad arloadoù brasoc\'h. Gallout a rit o kavout anezho war http://www.simplemobiletools.com + An arload-mañ a zo ul lodenn eus un heuliad arloadoù brasoc\'h. Gallout a rit o kavout anezho war https://www.simplemobiletools.com Opakovaná událost @@ -130,7 +131,7 @@ Přidat svátek Státní svátky Církevní svátky - Svátky byly úspěšně importovány do nového typu události + Holidays have been successfully imported into the \"Holidays\" event type Import některých událostí se nezdařil Import svátků nezdařil @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - Tato aplikace je součástí větší sady aplikací. Tyto a další aplikace najdete na http://www.simplemobiletools.com + Tato aplikace je součástí větší sady aplikací. Tyto a další aplikace najdete na https://www.simplemobiletools.com Gentagelse @@ -130,7 +131,7 @@ Tilføj helligdage Nationale helligdage Religiøse helligdage - Helligdage er importeret til en ny begivenhedstype + Helligdage er importeret til typen \"Helligdage\" Nogle helligdages blev ikke importeret Import af helligdage mislykkedes @@ -150,6 +151,7 @@ Erstat beskrivelse med sted Slet alle begivenheder Er du sikker på at du vil slette alle begivenheder? Dine begivenhedstyper og andre indstillinger vil forblive intakte. + Vis i tern CalDAV @@ -205,7 +207,7 @@ Adgang til kontaktpersoner er nødvendig hvis du vil importere fødselsdage. - Denne app er en enkelt del af en større serie apps. Du kan finde dem alle på siden http://www.simplemobiletools.com + Denne app er en enkelt del af en større serie apps. Du kan finde dem alle på siden https://www.simplemobiletools.com Wiederholung @@ -89,7 +90,6 @@ Erinnerung - Morgen vorher Füge eine weitere Erinnerung hinzu Ereignis Erinnerungen @@ -131,7 +131,7 @@ Feiertage hinzufügen Nationale Feiertage Religiöse Feiertage - Feiertage wurden erfolgreich in einen neuen Termintypen importiert + Feiertage sind erfolgreich in den \"Feiertage\"-Termintyp importiert worden Importieren einiger Termine fehlgeschlagen Importieren von Feiertagen fehlgeschlagen @@ -151,6 +151,7 @@ Ersetze Terminbeschreibung mit Ort Alle Termine löschen Bist du sicher, dass du alle Termine löschen willst? Deine Termintypen und Einstellungen bleiben erhalten. + Show a grid CalDAV @@ -207,7 +208,7 @@ Die Kontakte-Berechtigung wird nur für das Importieren von Geburtstagen benötigt. - Diese App ist nur eine aus einer größeren Serie von schlichten Apps. Der Rest davon findet sich auf http://www.simplemobiletools.com + Diese App ist nur eine aus einer größeren Serie von schlichten Apps. Der Rest davon findet sich auf https://www.simplemobiletools.com Repetición @@ -130,7 +131,7 @@ Añadir festivos Fiestas nacionales Fiestas regionales - Los días festivos se han importado correctamente a un nuevo tipo de evento + Holidays have been successfully imported into the \"Holidays\" event type Ha habido un fallo al importar eventos Ha habido un fallo al importar días festivos @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ El permiso de contactos se usa sólo para importar los cumpleaños de los contactos. - Esta aplicación es sólo una parte de una mayor serie de aplicaciones. Puede encontrar el resto en http://www.simplemobiletools.com + Esta aplicación es sólo una parte de una mayor serie de aplicaciones. Puede encontrar el resto en https://www.simplemobiletools.com Répétition @@ -130,7 +131,7 @@ Ajouter des jours fériés Jours fériés nationaux Jours fériés religieux - Les jours fériés ont été importés dans un nouveau type d\'événement avec succès + Holidays have been successfully imported into the \"Holidays\" event type L\'import de certains événements a échoué L\'import des jours fériés a échoué @@ -150,6 +151,7 @@ Remplacer la description de l\'événement par l\'emplacement Supprimer tous les événements Êtes-vous sûr de vouloir supprimer tous les événements ? Cela laissera vos types d\'événements et autres paramètres intacts. + Show a grid CalDAV @@ -205,7 +207,7 @@ La permission Contacts ne sert qu\'à importer l\'anniversaire de vos contacts. - Cette application est juste une pièce d\'une plus grosse série d\'applications. Vous pouvez trouver le reste de celles-ci à http://www.simplemobiletools.com + Cette application est juste une pièce d\'une plus grosse série d\'applications. Vous pouvez trouver le reste de celles-ci à https://www.simplemobiletools.com Repetición @@ -130,7 +131,7 @@ Engadir días festivos Festivo nacional Festas relixiosas - Engadíronse correctamente os días festivos nun novo tipo de evento + Holidays have been successfully imported into the \"Holidays\" event type Fallou a importación de algúns eventos Fallou a importación de días festivos @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ O permiso aos Contactos só é preciso se quere importar os cumpleanos dos contactos. - Esta aplicación é parte de unha serie máis grande de aplicativos. Puede atopar o resto en http://www.simplemobiletools.com + Esta aplicación é parte de unha serie máis grande de aplicativos. Puede atopar o resto en https://www.simplemobiletools.com दुहराव @@ -131,7 +132,7 @@ Add holidays National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -151,6 +152,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -206,7 +208,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com + This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com + Mjesečni raspored + Popis događaja kalendara + + + Događaj + Uredi događaj + Novi događaj + Naslov ne može biti prazan + Događaj ne može završiti prije nego što počne + Događaj je uspješno dodan + Događaj je uspješno ažuriran + Sortiranje prema vrsti događaja + Ispunite lokaciju za prikazivanje na karti + An event is upcoming + + + Ponavljanje + Nema ponavljanja + Dnevno + Tjedno + Mjesečno + Godišnje + tjedana + mjeseci + godina + Ponovi do + Zauvijek + Događaj je ponovljiv + Odabir sadrži ponavljajuće događaje + Izbriši samo odabrano ponavljanje + Izbriši sva ponavljanja + Ažuriraj samo odabrano ponavljanje + Ažuriraj sva ponavljanja + Ponovi do datuma + Ponovi x puta + Ponovi zauvijek + puta + Ponavljanje + Ponovi na + Svaki dan + Na odabrane dane + Isti dan + Zadnji dan + Ponovi isti dan svaki mjesec + Ponovi zadnjeg dana u mjesecu + Ponovi svaki + Svaki + prvi + drugi + treći + četvrti + peti + + + + Ponovi svaki + Svaki + prvi + drugi + treći + četvrti + peti + + + Rođendani + Dodaj rođendane za kontakt + Nisu pronađeni rođendani + Rođendani su uspješno dodani + + + Obljetnice + Dodaj obljetnice za kontakt + Nisu pronađene obljetnice + Obljetnice uspješno dodane + + + Podsjetnik + prije + Dodaj još jedan podsjetnik + Podsjetnici na događaj + + + Uvezi događaje + Izvezi događaje + Uvoz događaja iz .ics datoteke + Izvoz događaja u .ics datoteku + Zadani tip događaja + Izvoz prošlih događaja + Uključi vrste događaja + Naziv datoteke (bez .ics) + + + Naslov + Lokacija + Opis + Cjelodnevni + + + Tjedan + + + Vrste događaja + Dodaj novu vrstu + Uredi vrstu + Događaj s ovim naslovom već postoji + Boja + Redoviti događaj + Zadana vrsta događaja se ne može izbrisati + Odaberite vrstu događaja + Premjesti zahvaćene događaje u zadanu vrstu događaja + Trajno ukloni zahvaćene događaje + Da biste uklonili CalDAV kalendar, morate ga desinkronizirati + + + Praznici + Dodaj praznik + Nacionalni praznici + Vjerski praznici + Praznici su uspješno uvezeni u tip događaja \"Praznici\" + Uvoz nekih događaja nije uspio + Uvoz praznika nije uspio + + + Upravljanje vrstama događaja + Započni dan u + Završi dan u + Pokaži tjedne brojeve + Vibrirajte na obavijesti podsjetnika + Zvuk podsjetnika + Nije pronađena nijedna aplikacija koja može postaviti zvuk zvona + Nijedan + Dan ne može završiti prije nego što počne + CalDAV sinkronizacija + Popis događaja + Prikaži događaje iz prošlosti + Zamijenite opis događaja s lokacijom + Izbriši sve događaje + Jeste li sigurni da želite izbrisati sve događaje? To će ostaviti Vaše vrste događaja i druge postavke netaknutima. + Show a grid + + + CalDAV + Odaberi kalendare za sinkronizaciju + Upravljanje sinkroniziranim kalendarima + Pohrani samo lokalno + Osvježi CalDAV kalendare + Osvježavanje... + Osvježavanje završeno + Uređivanje kalendara nije uspjelo + Sinkronizacija... + Sinkronizacija završena + + + + Ponedjeljak + Utorak + Srijeda + Četvrtak + Petak + Subota + Nedjelja + + + Trening + Vježbanje nogu + Sastanak s Ivanom + U vrtu Rockstone + Knjižnica + Ručak s Marijom + U Plaza + Vrijeme za kavu + + + Kako mogu ukloniti praznike uvezene putem gumba \"Dodaj praznik\"? + Praznik koji je stvoren na taj način umetnut je u novu vrstu događaja pod nazivom \"Praznici\". Možete otići u Postavke -> Upravljanje vrstama događaja, + dugo pritisnite datu vrstu događaja i izbrišite ga odabirom koša za smeće. + Mogu li sinkronizirati događaje putem Google Kalendara ili druge usluge koje podržavaju CalDAV? + Da, samo uključite \"CalDAV sinkronizacija\" u postavkama aplikacije i odaberite kalendare koje želite sinkronizirati. Međutim, potrebna je aplikacija između Vašeg uređaja i poslužitelja. + U slučaju da želite sinkronizirati Google kalendar, njihova službena aplikacija Kalendar obavlja navedeni posao. Za ostale kalendare potreban Vam je aplikacija treće strane za upravljanje sinkronizacijom, na primjer DAVdroid. + + + + Jednostavan kalendar s događajima, prilagodljivim widget i bez oglasa. + + Jednostavan kalendar s dodatnom CalDAV sinkronizacijom. Možete jednostavno stvoriti ponavljajuće događaje i postaviti podsjetnike, također je moguće prikazati tjedne brojeve. + + Sadrži mjesečni prikaz i widget s popisom događaja gdje možete prilagoditi boju teksta, kao i transparentnost te boju pozadine. + + Ne sadrži oglase ili nepotrebne dozvole. Aplikacije je otvorenog koda, pruža prilagodljive boje. + + Dopuštenje za pohranu podataka potrebna je samo za izvoz ili uvoz događaja iz .ics datoteka. + + Dopuštenje za kontakte upotrebljava se samo pri uvozu rođendana i obljetnica kontakata. + + Ova je aplikacija samo dio većeg broja aplikacija. Možete pronaći ostatak na https://www.simplemobiletools.com + + + + diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 9d64ddb5e..f85131d4d 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -26,6 +26,7 @@ Bejegyzés frissítve Filter events by type Please fill in a location to be shown on a map + An event is upcoming Ismétlés @@ -130,7 +131,7 @@ Add holidays National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - Ez az alkalmazás egy nagyobb alkalmazás-sorozat része. A továbbiak itt találhatók: http://www.simplemobiletools.com + Ez az alkalmazás egy nagyobb alkalmazás-sorozat része. A továbbiak itt találhatók: https://www.simplemobiletools.com Calendario mensile - Calendar event list + Lista eventi calendario Evento @@ -26,6 +26,7 @@ Evento aggiornato correttamente Filtra eventi per tipologia Please fill in a location to be shown on a map + An event is upcoming Ripeti @@ -33,7 +34,7 @@ Giornalmente Settimanalmente Mensilmente - Annulamente + Annualmente Settimane Mesi Anni @@ -45,10 +46,10 @@ Elimina tutte le occorrenze Aggiorna solamente l\'occorenza selezionata Aggiorna tutte le occorenze - Repeat till a date + Ripeti fino a una data Ripeti x volte Ripeti per sempre - times + volte Ripeti Repeat on Ogni giorno @@ -76,80 +77,81 @@ last - Birthdays - Add contact birthdays - No birthdays have been found - Birthdays added successfully + Compleanni + Aggiungi compleanni contatti + Nessun compleanno trovato + Compleanni aggiunti con successo - Anniversaries - Add contact anniversaries - No anniversaries have been found - Anniversaries added successfully + Anniversari + Aggiungi anniversari contatti + Nessun anniversario trovato + Anniversari aggiunti con successo Promemoria - before - Add another reminder - Event reminders + prima + Aggiungi un altro promemoria + Promemoria eventi - Import events - Export events - Import events from an .ics file - Export events to an .ics file - Default event type - Export past events too - Include event types - Filename (without .ics) + Importa eventi + Esporta eventi + Importa eventi da un file .ics + Esporta eventi in un file .ics + Tipo evento predefinito + Esporta anche eventi passati + Includi tipi evento + Nome file (senza .ics) Titolo - Location + Posizione Descrizione All-day - Week + Settimana - Event types - Add a new type - Edit type - Type with this title already exists - Color - Regular event - Default event type cannot be deleted - Select an event type + Tipi evento + Aggiungi nuovo tipo + Modifica tipo + Un tipo con questo titolo esiste già + Colore + Evento regolare + Il tipo di evento predefinito non può essere cancellato + Seleziona un tipo di evento Move affected events into the default event type Permanently remove affected events - To remove a CalDAV calendar you have to unsynchronize it + Per rimuovere un calendario CalDAV devi desincronizzarlo - Holidays - Add holidays - National holidays - Religious holidays - Holidays have been successfully imported into a new event type - Importing some events failed - Importing holidays failed + Feste + Aggiungi feste + Feste nazionali + Feste religiose + Le feste sono state importate nel tipo evento \"Feste\" con successo + Importo di alcuni eventi fallito + Importo delle feste fallito - Manage event types + Gestisci tipi evento Start day at End day at - Show week numbers + Mostra numero settimane Vibrate on reminder notification - Reminder sound + Suoneria promemoria No app capable of setting ringtone found None - The day cannot end earlier than it starts + Il giorno non può finire prima che cominci CalDAV sync Event lists - Display events from the past - Replace event description with location - Delete all events - Are you sure you want to delete all events? This will leave your event types and other settings intact. + Mostra eventi passati + Sostituisci la descrizione con la posizione + Cancella tutti gli eventi + Vuoi cancellare tutti gli eventi? Questo lascerà i tuoi tipi evento e le altre impostazioni invariate. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com + This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com Repetition @@ -131,7 +132,7 @@ Add holidays National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -151,6 +152,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -195,7 +197,7 @@ לוח שנה פשוט עם אירועים, וויג\'ט בעיצוב אישי וללא פרסומות - לוח שנה אוף-ליין ללא כל אינטגרציה עם לוח שנה אחר. אפשר בקלות ליצור אירועים מחזוריים ותזכורות אליהם. יש אפשרות להציג את מספרי השבועות. מכיל וויג\'ט 4×4 שניתן להתאים אישית את גודלו, צבע הטקסט והרקע שלו, ושקיפותו. אין פרסומות או דרישות להרשאות בלתי-נחוצות. קוד המקור לגמרי פתוח, וניתן לעצב את צבעי הממשק.\nאפליקציה זו היא רק פיסה אחת מתוך סדרה גדולה יותר של אפליקציות. ניתן למצוא את השאר בכתובת http://www.simplemobiletools.com + לוח שנה אוף-ליין ללא כל אינטגרציה עם לוח שנה אחר. אפשר בקלות ליצור אירועים מחזוריים ותזכורות אליהם. יש אפשרות להציג את מספרי השבועות. מכיל וויג\'ט 4×4 שניתן להתאים אישית את גודלו, צבע הטקסט והרקע שלו, ושקיפותו. אין פרסומות או דרישות להרשאות בלתי-נחוצות. קוד המקור לגמרי פתוח, וניתן לעצב את צבעי הממשק.\nאפליקציה זו היא רק פיסה אחת מתוך סדרה גדולה יותר של אפליקציות. ניתן למצוא את השאר בכתובת https://www.simplemobiletools.com 繰り返し @@ -130,7 +131,7 @@ Add holidays National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - このアプリは、大きな一連のアプリの一つです。 他のアプリは http://www.simplemobiletools.com で見つけることができます + このアプリは、大きな一連のアプリの一つです。 他のアプリは https://www.simplemobiletools.com で見つけることができます 반복 @@ -130,7 +131,7 @@ 공휴일 추가 국경일 종교 휴일 - 공휴일을 새 이벤트 유형으로 가져왔습니다 + Holidays have been successfully imported into the \"Holidays\" event type 일부 이벤트를 가져오지 못했습니다 공휴일을 가져오는데 실패했습니다 @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - 이 앱은 더 큰 시리즈의 앱들 중 하나입니다. 나머지는 http://www.simplemobiletools.com 에서 찾을 수 있습니다. + 이 앱은 더 큰 시리즈의 앱들 중 하나입니다. 나머지는 https://www.simplemobiletools.com 에서 찾을 수 있습니다. Pasikartojimas @@ -130,7 +131,7 @@ Įtraukti atostogas Nacionalinės atostogos Religinės atostogos - Atostogos sėkmingai importuotos į naujo įvykio tipą + Atostogos buvo sėkmingai importuotos į \"Atostogos\" įvykio tipą Kai kurių įvykių importavimas nepavyko Atostogų importavimas nepavyko @@ -150,6 +151,7 @@ Pakeisti įvykio apibūdinimą į įvykio vietą Ištrinti visus įvykius Ar Jūs tikrai norite ištrinti visus įvykius? Tai neištrins įvykių tipų ir kitų nustatymų. + Show a grid CalDAV @@ -203,9 +205,9 @@ Saugyklos leidimas reikalingas tik eksportuoti ir importuoti įvykius iš .ics bylų. - Contaktų leidimas naudojamas tik importuoti kontaktų gimtadienius ir sukaktis. + Kontaktų leidimas naudojamas tik importuoti kontaktų gimtadienius ir sukaktis. - Ši programėle yra vienintelė iš keletos mūsų programėlių. Likusias Jūs galite rasti čia http://www.simplemobiletools.com + Ši programėle yra viena iš keletos mūsų programėlių. Likusias Jūs galite rasti čia https://www.simplemobiletools.com Gjentagelse @@ -130,7 +131,7 @@ Legg til off. fridager Nasjonale off. fridager Religiøse off. fridager - Off. fridager er vellykket importert til en ny hendelsestype + Off. fridager er vellykket importert til hendelsestypen \"Off. fridager\" Importering av noen hendelser feilet Importering av off. fridager feilet @@ -150,6 +151,7 @@ Erstatt hendelsesbeskrivelse med sted Slett alle hendelser Er du sikker på at du vil slette alle hendelser? Dine hendelsestyper og andre innstillinger blir beholdt. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com + This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com Herhaling @@ -130,7 +131,7 @@ Feestdagen toevoegen Nationale feestdagen Religieuze feestdagen - Feestdagen geïmporteerd als nieuw afspraaktype + De feestdagen zijn geïmporteerd onder afspraaktype \"Feestdagen\" Sommige feestdagen konden niet worden geïmporteerd Fout bij importeren van feestdagen @@ -150,6 +151,7 @@ Beschrijving vervangen door locatie van afspraken Alle afspraken verwijderen Alle afspraken verwijderen? Afspraaktypes en andere instellingen blijven behouden. + Rasterlijnen tonen CalDAV @@ -205,7 +207,7 @@ De machtiging voor contacten wordt alleen gebruikt voor het importeren van verjaardagen en feestdagen. - Deze app is onderdeel van een grotere verzameling. Vind de andere apps op http://www.simplemobiletools.com + Deze app is onderdeel van een grotere verzameling. Vind de andere apps op https://www.simplemobiletools.com Gjentagelse @@ -130,7 +131,7 @@ Legg til off. fridager Nasjonale off. fridager Religiøse off. fridager - Off. fridager er vellykket importert til en ny hendelsestype + Holidays have been successfully imported into the \"Holidays\" event type Importering av noen hendelser feilet Importering av off. fridager feilet @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com + This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com Powtórzenie @@ -130,7 +131,7 @@ Dodaj święto Święta państwowe Święta religijne - Święta zostały pomyślnie zaimportowane do nowego typu wydarzeń + Holidays have been successfully imported into the \"Holidays\" event type Importowanie niektórych świąt nie powiodło się Importowanie świąt nie powiodło się @@ -150,6 +151,7 @@    Zamieniaj opis wydarzenia na lokalizację    Usuń wszystkie wydarzenia    Czy na pewno mam usunąć wszystkie wydarzenia? Nie naruszy to typów wydarzeń i innych ustawień. + Show a grid CalDAV @@ -203,7 +205,7 @@ Uprawnienia kontaktów są używane tylko do importowania urodzin. - Niniejsza aplikacja jest tylko częścią naszej kolekcji prostych narzędzi. Ta, jak i pozostałe, dostępne są na stronie http://www.simplemobiletools.com + Niniejsza aplikacja jest tylko częścią naszej kolekcji prostych narzędzi. Ta, jak i pozostałe, dostępne są na stronie https://www.simplemobiletools.com Repetição @@ -130,7 +131,7 @@ Add holidays National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - Este aplicativo é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em http://www.simplemobiletools.com + Este aplicativo é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em https://www.simplemobiletools.com Repetição @@ -130,7 +131,7 @@ Adicionar feriados Feriados nacionais Feriados religiosos - Os feriados foram importados para um novo tipo de evento + Os feriados foram adicionados com sucesso para o tipo de evento \"Feriados\" Alguns eventos não foram importados Falha ao importar os feriados @@ -150,6 +151,7 @@ Substituir descrição pela localização do evento Apagar todos os eventos Tem a certeza de que deseja apagar todos os eventos? Esta ação não afeta os tipos de eventos nem as outras definições. + Show a grid CalDAV @@ -205,7 +207,7 @@ A permissão aos contactos apenas é utilizada para importar os aniversários. - Esta aplicação é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em http://www.simplemobiletools.com + Esta aplicação é apenas parte de um conjunto mais vasto de aplicações. Saiba mais em https://www.simplemobiletools.com Повторять @@ -130,7 +131,7 @@ Добавить праздники Национальные праздники Религиозные праздники - Праздники были успешно импортированы в новый тип событий + Holidays have been successfully imported into the \"Holidays\" event type Не удалось импортировать некоторые события Не удалось импортировать праздники @@ -150,6 +151,7 @@ Заменить описание события местоположением Удалить все события Вы действительно хотите удалить все события? Это не затронет ваши типы событий и другие настройки. + Show a grid CalDAV @@ -205,7 +207,7 @@ Разрешение на чтение контактов требуется только при импорте дней рождения. - Это приложение является лишь частью крупной серии приложений. Вы можете найти остальные на http://www.simplemobiletools.com + Это приложение является лишь частью крупной серии приложений. Вы можете найти остальные на https://www.simplemobiletools.com Opakovanie @@ -121,9 +122,6 @@ Bežná udalosť Predvolený typ nie je možné vymazať Zvoľte typ udalosti - Sviatky boli úspešne importované do nového typu udalostí - Importovanie niektorých sviatkov zlyhalo - Importovanie udalostí zlyhalo Presunúť dotknuté udalosti do predvoleného typu udalostí Natrvalo odstrániť dotknuté udalosti Na odstránenie CalDAV kalendára musíte vypnúť jeho synchronizáciu @@ -133,6 +131,9 @@ Pridať sviatky Štátne sviatky Náboženské sviatky + Sviatky boli úspešne importované do nového typu udalostí \"Sviatky\" + Importovanie niektorých sviatkov zlyhalo + Importovanie udalostí zlyhalo Spravovať typy udalostí @@ -150,6 +151,7 @@ Nahradiť popis udalosti miestom Odstrániť všetky udalosti Ste si istý, že chcete odstrániť všetky udalosti? Všetky typy udalostí a nastavenia ostanú nedotknuté. + Zobraziť mriežku CalDAV @@ -205,7 +207,7 @@ Oprávnenie ku kontaktom je používané iba pri pridávaní narodenín alebo výročí kontaktov. - Táto aplikácia je iba jednou zo skupiny aplikácií. Ostatné viete nájsť na http://www.simplemobiletools.com + Táto aplikácia je iba jednou zo skupiny aplikácií. Ostatné viete nájsť na https://www.simplemobiletools.com Upprepning @@ -130,7 +131,7 @@ Lägg till helgdagar Nationella helgdagar Religiösa helgdagar - Helgdagar har importerats till en ny händelsetyp + Holidays have been successfully imported into the \"Holidays\" event type Vissa händelser kunde inte importeras Helgdagar kunde inte importeras @@ -150,6 +151,7 @@ Ersätt händelsebeskrivning med plats Ta bort alla händelser Är du säker på att du vill ta bort alla händelser? Dina händelsetyper och andra inställningar påverkas inte av borttagningen. + Show a grid CalDAV @@ -205,7 +207,7 @@ Kontaktbehörigheten används bara för att importera kontakters födelsedagar och årsdagar. - Denna app är bara en del av en större serie appar. Du hittar resten av dem på http://www.simplemobiletools.com + Denna app är bara en del av en större serie appar. Du hittar resten av dem på https://www.simplemobiletools.com Tekrarla @@ -130,7 +131,7 @@ Tatil ekle National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -150,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -205,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanını şu adresten bulabilirsiniz: http://www.simplemobiletools.com + Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanını şu adresten bulabilirsiniz: https://www.simplemobiletools.com 重複 @@ -130,7 +131,7 @@ 添加節日 國定節日 宗教節日 - 節日成功匯入新活動類型 + 節日已成功匯入"節日"活動類型 匯入一些活動失敗 匯入節日失敗 @@ -150,6 +151,7 @@ 活動描述改成地點 刪除全部活動 你確定要刪除全部活動嗎?會完整留下你的活動類型和其他設定。 + Show a grid CalDAV @@ -205,7 +207,7 @@ 聯絡人權限只是用來匯入聯絡人生日和紀念日。 - 這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 http://www.simplemobiletools.com + 這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 https://www.simplemobiletools.com + Reworked custom notification sound picking, should be more reliable Added a Location field\n Allow adding Contact birthdays diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b8e8cd71..ef70f77ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ Event updated successfully Filter events by type Please fill in a location to be shown on a map + An event is upcoming Repetition @@ -89,7 +90,6 @@ Reminder - Tomorrow before Add another reminder Event reminders @@ -131,7 +131,7 @@ Add holidays National holidays Religious holidays - Holidays have been successfully imported into a new event type + Holidays have been successfully imported into the \"Holidays\" event type Importing some events failed Importing holidays failed @@ -151,6 +151,7 @@ Replace event description with location Delete all events Are you sure you want to delete all events? This will leave your event types and other settings intact. + Show a grid CalDAV @@ -206,7 +207,7 @@ The Contacts permission is used only at importing contact birthdays and anniversaries. - This app is just one piece of a bigger series of apps. You can find the rest of them at http://www.simplemobiletools.com + This app is just one piece of a bigger series of apps. You can find the rest of them at https://www.simplemobiletools.com