mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Compare commits
20 Commits
9ed4630f9b
...
516bf394f8
| Author | SHA1 | Date | |
|---|---|---|---|
| 516bf394f8 | |||
| 2816b7c3d0 | |||
| a9acbd6cab | |||
| e121f46b61 | |||
| d57de9d10c | |||
| e4348a2144 | |||
| e608c6ea62 | |||
| 5403b6bd51 | |||
| a6cf43dbca | |||
| 074627f6e1 | |||
|
|
96e20f751f | ||
| 0daa4f6a2f | |||
| 035b392ece | |||
| 648c7277cf | |||
| 5006f5128b | |||
| 97b98a872d | |||
| 862a851e1c | |||
| 804030f5c0 | |||
| 08ab3c22ce | |||
| b58f836d8e |
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,32 +1,54 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.3.1] -- 2025-08-13
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Add notes to exported CSV files (@iSoron)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Prevent some views from being obscured by system UI (@iSoron, #2171)
|
||||||
|
- Disable confetti if animations are disabled globally (@iSoron, #2170)
|
||||||
|
- Make symbols easier to distinguish in "pure black" dark mode (powerjungle, #2136)
|
||||||
|
- Trim unit labels when necessary (@hiqua, @iSoron, #2158)
|
||||||
|
|
||||||
## [2.3.0] -- 2025-06-23
|
## [2.3.0] -- 2025-06-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add support for Android 15 and 16 (@iSoron)
|
- Add support for Android 15 and 16 (@iSoron)
|
||||||
- Show confetti animation (@gokulk16, @iSoron, #1743)
|
- Show confetti animation (@gokulk16, @iSoron, #1743)
|
||||||
- Show streaks for measurable habits (@teckwarz, #2059)
|
- Show streaks for measurable habits (@teckwarz, #2059)
|
||||||
- Allow user to unset measurable habits (@leontodd, @kalina559, #1899, #2109)
|
- Allow user to unset measurable habits (@leontodd, @kalina559, #1899, #2109)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Change background widget color for habits with implicit checks (@wobbba, #1915)
|
- Change background widget color for habits with implicit checks (@wobbba, #1915)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix notification when goal type is set to maximum (@manish99verma, #1931)
|
- Fix notification when goal type is set to maximum (@manish99verma, #1931)
|
||||||
- Never mark "at most" habits as completed for the day (@kalina559, #2077)
|
- Never mark "at most" habits as completed for the day (@kalina559, #2077)
|
||||||
- Increase minimum widget size (@iSoron, #2118)
|
- Increase minimum widget size (@iSoron, #2118)
|
||||||
- Improve Gradle configuration (@jimlyas, #2108)
|
- Improve Gradle configuration (@jimlyas, #2108)
|
||||||
|
|
||||||
## [2.2.0] -- 2024-01-30
|
## [2.2.0] -- 2024-01-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add support for Android 14 (@iSoron, @hiqua)
|
- Add support for Android 14 (@iSoron, @hiqua)
|
||||||
- Allow user to change app language (@leondzn)
|
- Allow user to change app language (@leondzn)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Implement workaround to make notifications non-dismissible in Android 14 (@iSoron, #1872)
|
- Implement workaround to make notifications non-dismissible in Android 14 (@iSoron, #1872)
|
||||||
- Fix splash screen background color in dark mode (@SIKV, #1888)
|
- Fix splash screen background color in dark mode (@SIKV, #1888)
|
||||||
|
|
||||||
## [2.1.3] -- 2023-08-28
|
## [2.1.3] -- 2023-08-28
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Use text input on Samsung devices (@iSoron, #1719)
|
- Use text input on Samsung devices (@iSoron, #1719)
|
||||||
- Prevent crash if alarm permission is revoked (@iSoron)
|
- Prevent crash if alarm permission is revoked (@iSoron)
|
||||||
- Adjust widget colors (@iSoron)
|
- Adjust widget colors (@iSoron)
|
||||||
@@ -34,7 +56,9 @@
|
|||||||
- Fix skip button in locales that use comma instead of dot (@iSoron, #1721)
|
- Fix skip button in locales that use comma instead of dot (@iSoron, #1721)
|
||||||
|
|
||||||
## [2.1.2] -- 2023-05-26
|
## [2.1.2] -- 2023-05-26
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix bug that caused widget to enter checkmark on wrong date (@iSoron, #1541)
|
- Fix bug that caused widget to enter checkmark on wrong date (@iSoron, #1541)
|
||||||
- Fix widget corners on Android 12 (@iSoron)
|
- Fix widget corners on Android 12 (@iSoron)
|
||||||
- Fix bug that caused notes to be lost when editing a checkmark (@iSoron, #1566)
|
- Fix bug that caused notes to be lost when editing a checkmark (@iSoron, #1566)
|
||||||
@@ -42,18 +66,23 @@
|
|||||||
- Accept comma (instead of dot) in certain locales (@iSoron)
|
- Accept comma (instead of dot) in certain locales (@iSoron)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Remove update delay after entering a checkmark (@iSoron)
|
- Remove update delay after entering a checkmark (@iSoron)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Remove stack widgets (@iSoron)
|
- Remove stack widgets (@iSoron)
|
||||||
|
|
||||||
|
|
||||||
## [2.1.1] -- 2022-09-24
|
## [2.1.1] -- 2022-09-24
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix Tasker plugin (@iSoron, #1503)
|
- Fix Tasker plugin (@iSoron, #1503)
|
||||||
|
|
||||||
## [2.1.0] -- 2022-09-10
|
## [2.1.0] -- 2022-09-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Allow user to add notes to specific dates (@vbh, #1103)
|
- Allow user to add notes to specific dates (@vbh, #1103)
|
||||||
- Allow user to track "at most" numerical habits (@KristianTashkov, #1101)
|
- Allow user to track "at most" numerical habits (@KristianTashkov, #1101)
|
||||||
- Allow user to add skips to measurable habits (@kalina559, #1319)
|
- Allow user to add skips to measurable habits (@kalina559, #1319)
|
||||||
@@ -64,10 +93,12 @@
|
|||||||
- Add support for Android 13 themed icons (@cheeeeer, #1497)
|
- Add support for Android 13 themed icons (@cheeeeer, #1497)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Hide snooze button Android 12 notifications (@hiqua, #1226)
|
- Hide snooze button Android 12 notifications (@hiqua, #1226)
|
||||||
- Remove preference to set LED lights (@iSoron)
|
- Remove preference to set LED lights (@iSoron)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Hide failed habits along with completed ones (@hiqua, #1052)
|
- Hide failed habits along with completed ones (@hiqua, #1052)
|
||||||
- Cycle through all checkmark states when toggling (@iSoron)
|
- Cycle through all checkmark states when toggling (@iSoron)
|
||||||
- Add delay after toggling a habit (@hiqua, @kalina559, #1147)
|
- Add delay after toggling a habit (@hiqua, @kalina559, #1147)
|
||||||
@@ -76,6 +107,7 @@
|
|||||||
- Increase target SDK to 31 (@hiqua)
|
- Increase target SDK to 31 (@hiqua)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix small dialog buttons (@kalina559, #1096)
|
- Fix small dialog buttons (@kalina559, #1096)
|
||||||
- Fix invalid CSV files (@hiqua, #1177)
|
- Fix invalid CSV files (@hiqua, #1177)
|
||||||
- Fix small issues in calendar chart (@kalina559, #1314)
|
- Fix small issues in calendar chart (@kalina559, #1314)
|
||||||
@@ -84,13 +116,16 @@
|
|||||||
- Fix widgets not working correctly on API 33 (@iSoron, #1488)
|
- Fix widgets not working correctly on API 33 (@iSoron, #1488)
|
||||||
|
|
||||||
### Refactoring & Testing
|
### Refactoring & Testing
|
||||||
|
|
||||||
- Replace raster icons by vector assets (@kalina559)
|
- Replace raster icons by vector assets (@kalina559)
|
||||||
- Remove JVM dependencies from uhabits-core module (@sgallese)
|
- Remove JVM dependencies from uhabits-core module (@sgallese)
|
||||||
- Add various missing tests (@sgallese)
|
- Add various missing tests (@sgallese)
|
||||||
- Upgrade project dependencies (@hiqua, @sgallese)
|
- Upgrade project dependencies (@hiqua, @sgallese)
|
||||||
|
|
||||||
## [2.0.3] - 2021-08-21
|
## [2.0.3] - 2021-08-21
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Improve automatic checkmarks for monthly habits (@iSoron, #947)
|
- Improve automatic checkmarks for monthly habits (@iSoron, #947)
|
||||||
- Fix small theme issues (@iSoron)
|
- Fix small theme issues (@iSoron)
|
||||||
- Fix ANR on some Samsung phones (@iSoron, #962)
|
- Fix ANR on some Samsung phones (@iSoron, #962)
|
||||||
@@ -102,9 +137,11 @@
|
|||||||
## [2.0.2] - 2021-05-23
|
## [2.0.2] - 2021-05-23
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Make checkmark widget resizable
|
- Make checkmark widget resizable
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fix crash caused by numerical habits with zero target (@iSoron, #903)
|
- Fix crash caused by numerical habits with zero target (@iSoron, #903)
|
||||||
- Fix small issues with font size (@iSoron)
|
- Fix small issues with font size (@iSoron)
|
||||||
- Allow fractional target values (@sumanabhi, #911)
|
- Allow fractional target values (@sumanabhi, #911)
|
||||||
@@ -115,18 +152,22 @@
|
|||||||
## [2.0.1] - 2021-05-09
|
## [2.0.1] - 2021-05-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Make midnight delay optional and disabled by default (@hiqua)
|
- Make midnight delay optional and disabled by default (@hiqua)
|
||||||
- Add arrows to sort menu (@iSoron)
|
- Add arrows to sort menu (@iSoron)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Temporarily remove experimental device sync functionality. This feature will be re-added in
|
- Temporarily remove experimental device sync functionality. This feature will be re-added in
|
||||||
Loop 2.1.
|
Loop 2.1.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Make implicit checkmarks easier to read (@iSoron)
|
- Make implicit checkmarks easier to read (@iSoron)
|
||||||
- Update and improve list of translators (@hiqua, @iSoron)
|
- Update and improve list of translators (@hiqua, @iSoron)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Disable transparency for stacked widgets (@hiqua)
|
- Disable transparency for stacked widgets (@hiqua)
|
||||||
- Fix various color issues on the dark theme (@hiqua, @iSoron)
|
- Fix various color issues on the dark theme (@hiqua, @iSoron)
|
||||||
- Fix "customize notifications" on older devices (@hiqua)
|
- Fix "customize notifications" on older devices (@hiqua)
|
||||||
@@ -135,6 +176,7 @@
|
|||||||
- Fix checkmark widget not rendering properly on some Samsung phones (@iSoron)
|
- Fix checkmark widget not rendering properly on some Samsung phones (@iSoron)
|
||||||
|
|
||||||
### Refactoring & Testing
|
### Refactoring & Testing
|
||||||
|
|
||||||
- Finish conversion of the entire project to Kotlin (@hiqua, @iSoron, @MarKco)
|
- Finish conversion of the entire project to Kotlin (@hiqua, @iSoron, @MarKco)
|
||||||
- Automatically run large tests on GitHub Actions (@iSoron)
|
- Automatically run large tests on GitHub Actions (@iSoron)
|
||||||
- Remove unused v21 resources (@hiqua)
|
- Remove unused v21 resources (@hiqua)
|
||||||
@@ -142,6 +184,7 @@
|
|||||||
## [2.0.0-alpha] - 2020-11-29
|
## [2.0.0-alpha] - 2020-11-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Track numeric habits (@iSoron, @namnl)
|
- Track numeric habits (@iSoron, @namnl)
|
||||||
- Skip days without breaking streak (@KristianTashkov)
|
- Skip days without breaking streak (@KristianTashkov)
|
||||||
- Sort habits by status (@hiqua)
|
- Sort habits by status (@hiqua)
|
||||||
@@ -152,15 +195,18 @@
|
|||||||
- Export backups daily (@iSoron)
|
- Export backups daily (@iSoron)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Drop support to devices older than Android 6.0 (API 23)
|
- Drop support to devices older than Android 6.0 (API 23)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Reset chart offset when switching scale (@alxmjo)
|
- Reset chart offset when switching scale (@alxmjo)
|
||||||
- Don't show reminders from archived habits (@KristianTashkov)
|
- Don't show reminders from archived habits (@KristianTashkov)
|
||||||
- Lapses on non-daily habits decrease the score too much (@iSoron)
|
- Lapses on non-daily habits decrease the score too much (@iSoron)
|
||||||
- Update widgets at midnight (@KristianTashkov)
|
- Update widgets at midnight (@KristianTashkov)
|
||||||
|
|
||||||
### Refactoring
|
### Refactoring
|
||||||
|
|
||||||
- Convert files to Kotlin (@olegivo)
|
- Convert files to Kotlin (@olegivo)
|
||||||
|
|
||||||
## [1.8.12] - 2021-01-30
|
## [1.8.12] - 2021-01-30
|
||||||
@@ -185,13 +231,15 @@
|
|||||||
|
|
||||||
## [1.8.8] - 2020-06-21
|
## [1.8.8] - 2020-06-21
|
||||||
|
|
||||||
- Make small changes to the habit scheduling algorithm, so that "1 time every x days" habits work more predictably.
|
- Make small changes to the habit scheduling algorithm, so that "1 time every x days" habits work
|
||||||
|
more predictably.
|
||||||
- Fix crash when saving habit
|
- Fix crash when saving habit
|
||||||
|
|
||||||
## [1.8.0] - 2020-01-01
|
## [1.8.0] - 2020-01-01
|
||||||
|
|
||||||
- New bar chart showing number of repetitions performed in each week, month, quarter or year.
|
- New bar chart showing number of repetitions performed in each week, month, quarter or year.
|
||||||
- Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will no longer break your streak.
|
- Improved calculation of streaks for non-daily habits: performing habits on irregular weekdays will
|
||||||
|
no longer break your streak.
|
||||||
- Many more colors to choose from (now 20 in total).
|
- Many more colors to choose from (now 20 in total).
|
||||||
- Ability to customize how transparent the widgets are on your home screen.
|
- Ability to customize how transparent the widgets are on your home screen.
|
||||||
- Ability to customize the first day of the week.
|
- Ability to customize the first day of the week.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Updating gradle might fix this, so try again in the future to remove this and ru
|
|||||||
If this doesn't produce any warning, try to remove it.
|
If this doesn't produce any warning, try to remove it.
|
||||||
*/
|
*/
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(11)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -44,8 +44,8 @@ android {
|
|||||||
compileSdk = 36
|
compileSdk = 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode = 20300
|
versionCode = 20301
|
||||||
versionName = "2.3.0"
|
versionName = "2.3.1"
|
||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
applicationId = "org.isoron.uhabits"
|
applicationId = "org.isoron.uhabits"
|
||||||
@@ -79,11 +79,11 @@ android {
|
|||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
isCoreLibraryDesugaringEnabled = true
|
isCoreLibraryDesugaringEnabled = true
|
||||||
targetCompatibility(JavaVersion.VERSION_11)
|
targetCompatibility(JavaVersion.VERSION_17)
|
||||||
sourceCompatibility(JavaVersion.VERSION_11)
|
sourceCompatibility(JavaVersion.VERSION_17)
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions.jvmTarget = JavaVersion.VERSION_11.toString()
|
kotlinOptions.jvmTarget = JavaVersion.VERSION_17.toString()
|
||||||
buildFeatures.viewBinding = true
|
buildFeatures.viewBinding = true
|
||||||
lint.abortOnError = false
|
lint.abortOnError = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package org.isoron.uhabits.activities.habits.list.views
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.filters.MediumTest
|
import androidx.test.filters.MediumTest
|
||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.junit.Before
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@@ -35,23 +35,22 @@ class EmptyListViewTest : BaseViewTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val path = "habits/list/EmptyListView"
|
private val path = "habits/list/EmptyListView"
|
||||||
private val view: EmptyListView = EmptyListView(targetContext)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
override fun setUp() {
|
|
||||||
super.setUp()
|
|
||||||
measureView(view, dpToPixels(200), dpToPixels(200))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("non-deterministic failure")
|
||||||
fun testRender_done() {
|
fun testRender_done() {
|
||||||
|
val view = EmptyListView(targetContext)
|
||||||
view.showDone()
|
view.showDone()
|
||||||
|
measureView(view, dpToPixels(200), dpToPixels(200))
|
||||||
assertRenders(view, "$path/done.png")
|
assertRenders(view, "$path/done.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore("non-deterministic failure")
|
||||||
fun testRender_empty() {
|
fun testRender_empty() {
|
||||||
|
val view = EmptyListView(targetContext)
|
||||||
view.showEmpty()
|
view.showEmpty()
|
||||||
|
measureView(view, dpToPixels(200), dpToPixels(200))
|
||||||
assertRenders(view, "$path/empty.png")
|
assertRenders(view, "$path/empty.png")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.isoron.uhabits.R
|
|||||||
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
|
import org.isoron.uhabits.core.utils.DateUtils.Companion.getTodayWithOffset
|
||||||
import org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor
|
import org.isoron.uhabits.utils.PaletteUtils.getAndroidTestColor
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -57,6 +58,7 @@ class CheckmarkWidgetViewTest : BaseViewTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
|
@Ignore("non-deterministic")
|
||||||
fun testRender_checked() {
|
fun testRender_checked() {
|
||||||
assertRenders(view, PATH + "checked.png")
|
assertRenders(view, PATH + "checked.png")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.isoron.uhabits.BuildConfig
|
|||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.databinding.AboutBinding
|
import org.isoron.uhabits.databinding.AboutBinding
|
||||||
|
import org.isoron.uhabits.utils.applyBottomInset
|
||||||
import org.isoron.uhabits.utils.applyRootViewInsets
|
import org.isoron.uhabits.utils.applyRootViewInsets
|
||||||
import org.isoron.uhabits.utils.currentTheme
|
import org.isoron.uhabits.utils.currentTheme
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
@@ -55,6 +56,7 @@ class AboutView(
|
|||||||
binding.tvTranslate.setOnClickListener { screen.showTranslationWebsite() }
|
binding.tvTranslate.setOnClickListener { screen.showTranslationWebsite() }
|
||||||
binding.tvVersion.setOnClickListener { screen.onPressDeveloperCountdown() }
|
binding.tvVersion.setOnClickListener { screen.onPressDeveloperCountdown() }
|
||||||
binding.tvVersion.text = String.format(version, BuildConfig.VERSION_NAME)
|
binding.tvVersion.text = String.format(version, BuildConfig.VERSION_NAME)
|
||||||
|
binding.outerLinearLayout.applyBottomInset()
|
||||||
applyRootViewInsets()
|
applyRootViewInsets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ class RingView : View {
|
|||||||
val res = StyledResources(context)
|
val res = StyledResources(context)
|
||||||
if (backgroundColor == null) backgroundColor = res.getColor(R.attr.cardBgColor)
|
if (backgroundColor == null) backgroundColor = res.getColor(R.attr.cardBgColor)
|
||||||
if (inactiveColor == null) inactiveColor = res.getColor(R.attr.contrast100)
|
if (inactiveColor == null) inactiveColor = res.getColor(R.attr.contrast100)
|
||||||
inactiveColor = setAlpha(inactiveColor!!, 0.1f)
|
inactiveColor = setAlpha(inactiveColor!!, 0.15f)
|
||||||
rect = RectF()
|
rect = RectF()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
import nl.dionsegijn.konfetti.core.Party
|
import nl.dionsegijn.konfetti.core.Party
|
||||||
@@ -226,6 +227,14 @@ class ListHabitsScreen
|
|||||||
override fun showConfetti(color: PaletteColor, x: Float, y: Float) {
|
override fun showConfetti(color: PaletteColor, x: Float, y: Float) {
|
||||||
if (x == 0f && y == 0f) return
|
if (x == 0f && y == 0f) return
|
||||||
if (preferences.isConfettiAnimationDisabled) return
|
if (preferences.isConfettiAnimationDisabled) return
|
||||||
|
if (Settings.Global.getFloat(
|
||||||
|
activity.contentResolver,
|
||||||
|
Settings.Global.ANIMATOR_DURATION_SCALE,
|
||||||
|
1f
|
||||||
|
) == 0f
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
val baseColor = themeSwitcher.currentTheme!!.color(color).toInt()
|
val baseColor = themeSwitcher.currentTheme!!.color(color).toInt()
|
||||||
rootView.get().konfettiView.start(
|
rootView.get().konfettiView.start(
|
||||||
Party(
|
Party(
|
||||||
@@ -299,30 +308,36 @@ class ListHabitsScreen
|
|||||||
command.selected.size
|
command.selected.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ChangeHabitColorCommand -> {
|
is ChangeHabitColorCommand -> {
|
||||||
return activity.resources.getQuantityString(
|
return activity.resources.getQuantityString(
|
||||||
R.plurals.toast_habits_changed,
|
R.plurals.toast_habits_changed,
|
||||||
command.selected.size
|
command.selected.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is CreateHabitCommand -> {
|
is CreateHabitCommand -> {
|
||||||
return activity.resources.getString(R.string.toast_habit_created)
|
return activity.resources.getString(R.string.toast_habit_created)
|
||||||
}
|
}
|
||||||
|
|
||||||
is DeleteHabitsCommand -> {
|
is DeleteHabitsCommand -> {
|
||||||
return activity.resources.getQuantityString(
|
return activity.resources.getQuantityString(
|
||||||
R.plurals.toast_habits_deleted,
|
R.plurals.toast_habits_deleted,
|
||||||
command.selected.size
|
command.selected.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is EditHabitCommand -> {
|
is EditHabitCommand -> {
|
||||||
return activity.resources.getQuantityString(R.plurals.toast_habits_changed, 1)
|
return activity.resources.getQuantityString(R.plurals.toast_habits_changed, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
is UnarchiveHabitsCommand -> {
|
is UnarchiveHabitsCommand -> {
|
||||||
return activity.resources.getQuantityString(
|
return activity.resources.getQuantityString(
|
||||||
R.plurals.toast_habits_unarchived,
|
R.plurals.toast_habits_unarchived,
|
||||||
command.selected.size
|
command.selected.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,9 +350,11 @@ class ListHabitsScreen
|
|||||||
adapter.refresh()
|
adapter.refresh()
|
||||||
activity.showMessage(activity.resources.getString(R.string.habits_imported))
|
activity.showMessage(activity.resources.getString(R.string.habits_imported))
|
||||||
}
|
}
|
||||||
|
|
||||||
ImportDataTask.NOT_RECOGNIZED -> {
|
ImportDataTask.NOT_RECOGNIZED -> {
|
||||||
activity.showMessage(activity.resources.getString(R.string.file_not_recognized))
|
activity.showMessage(activity.resources.getString(R.string.file_not_recognized))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
activity.showMessage(activity.resources.getString(R.string.could_not_import))
|
activity.showMessage(activity.resources.getString(R.string.could_not_import))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,14 @@
|
|||||||
package org.isoron.uhabits.activities.habits.list.views
|
package org.isoron.uhabits.activities.habits.list.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Rect
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.GestureDetector
|
import android.view.GestureDetector
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper.DOWN
|
import androidx.recyclerview.widget.ItemTouchHelper.DOWN
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper.END
|
import androidx.recyclerview.widget.ItemTouchHelper.END
|
||||||
@@ -57,6 +60,7 @@ class HabitCardListView(
|
|||||||
) : RecyclerView(context, null, R.attr.scrollableRecyclerViewStyle) {
|
) : RecyclerView(context, null, R.attr.scrollableRecyclerViewStyle) {
|
||||||
|
|
||||||
var checkmarkCount: Int = 0
|
var checkmarkCount: Int = 0
|
||||||
|
private var insetDecorationsAdded: Boolean = false
|
||||||
|
|
||||||
var dataOffset: Int = 0
|
var dataOffset: Int = 0
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -75,9 +79,32 @@ class HabitCardListView(
|
|||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
isLongClickable = true
|
isLongClickable = true
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
|
applyBottomInset()
|
||||||
super.setAdapter(adapter)
|
super.setAdapter(adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun applyBottomInset() {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
||||||
|
if (insetDecorationsAdded) return@setOnApplyWindowInsetsListener insets
|
||||||
|
insetDecorationsAdded = true
|
||||||
|
val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
addItemDecoration(object : ItemDecoration() {
|
||||||
|
override fun getItemOffsets(
|
||||||
|
outRect: Rect,
|
||||||
|
view: View,
|
||||||
|
parent: RecyclerView,
|
||||||
|
state: State
|
||||||
|
) {
|
||||||
|
val itemCount = parent.adapter?.itemCount
|
||||||
|
if (parent.getChildAdapterPosition(view) == itemCount?.minus(1)) {
|
||||||
|
outRect.bottom = systemBarsInsets.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
insets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createHabitCardView(): HabitCardView {
|
fun createHabitCardView(): HabitCardView {
|
||||||
return cardViewFactory.create()
|
return cardViewFactory.create()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,17 +238,18 @@ class HabitCardView(
|
|||||||
|
|
||||||
private fun getAbsoluteButtonLocation(timestamp: Timestamp): PointF {
|
private fun getAbsoluteButtonLocation(timestamp: Timestamp): PointF {
|
||||||
val containerLocation = IntArray(2)
|
val containerLocation = IntArray(2)
|
||||||
this.getLocationOnScreen(containerLocation)
|
this.getLocationInWindow(containerLocation)
|
||||||
val relButtonLocation = getRelativeButtonLocation(timestamp)
|
val relButtonLocation = getRelativeButtonLocation(timestamp)
|
||||||
val windowInsets = rootWindowInsets
|
val windowInsets = rootWindowInsets
|
||||||
val statusBarHeight = if (SDK_INT <= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
val xInset = windowInsets?.displayCutout?.safeInsetLeft ?: 0
|
||||||
|
val yInset = if (SDK_INT <= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||||
windowInsets?.systemWindowInsetTop ?: 0
|
windowInsets?.systemWindowInsetTop ?: 0
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
return PointF(
|
return PointF(
|
||||||
containerLocation[0].toFloat() + relButtonLocation.x,
|
containerLocation[0].toFloat() + relButtonLocation.x - xInset,
|
||||||
containerLocation[1].toFloat() + relButtonLocation.y - statusBarHeight
|
containerLocation[1].toFloat() + relButtonLocation.y - yInset
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,16 +189,19 @@ class NumberButtonView(
|
|||||||
textSize = dim(R.dimen.smallTextSize)
|
textSize = dim(R.dimen.smallTextSize)
|
||||||
typeface = getFontAwesome()
|
typeface = getFontAwesome()
|
||||||
}
|
}
|
||||||
|
|
||||||
value >= 0 -> {
|
value >= 0 -> {
|
||||||
label = value.toShortString()
|
label = value.toShortString()
|
||||||
typeface = BOLD_TYPEFACE
|
typeface = BOLD_TYPEFACE
|
||||||
textSize = dim(R.dimen.smallTextSize)
|
textSize = dim(R.dimen.smallTextSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.areQuestionMarksEnabled -> {
|
preferences.areQuestionMarksEnabled -> {
|
||||||
label = resources.getString(R.string.fa_question)
|
label = resources.getString(R.string.fa_question)
|
||||||
typeface = getFontAwesome()
|
typeface = getFontAwesome()
|
||||||
textSize = dim(R.dimen.smallerTextSize)
|
textSize = dim(R.dimen.smallerTextSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
label = "0"
|
label = "0"
|
||||||
typeface = BOLD_TYPEFACE
|
typeface = BOLD_TYPEFACE
|
||||||
@@ -212,14 +215,23 @@ class NumberButtonView(
|
|||||||
pUnit.color = activeColor
|
pUnit.color = activeColor
|
||||||
|
|
||||||
if (units.isBlank()) {
|
if (units.isBlank()) {
|
||||||
|
// Draw number without units
|
||||||
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
||||||
rect.offset(0f, 0.5f * em)
|
rect.offset(0f, 0.5f * em)
|
||||||
canvas.drawText(label, rect.centerX(), rect.centerY(), pNumber)
|
canvas.drawText(label, rect.centerX(), rect.centerY(), pNumber)
|
||||||
} else {
|
} else {
|
||||||
|
// Draw number
|
||||||
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
rect.set(0f, 0f, width.toFloat(), height.toFloat())
|
||||||
canvas.drawText(label, rect.centerX(), rect.centerY(), pNumber)
|
canvas.drawText(label, rect.centerX(), rect.centerY(), pNumber)
|
||||||
|
|
||||||
|
// Draw units
|
||||||
|
val maxUnitsWidth = width * 0.9f
|
||||||
|
var trimmedUnits = units
|
||||||
|
while (trimmedUnits.length > 2 && pUnit.measureText(trimmedUnits) > maxUnitsWidth) {
|
||||||
|
trimmedUnits = trimmedUnits.dropLast(2) + "…"
|
||||||
|
}
|
||||||
rect.offset(0f, 1.3f * em)
|
rect.offset(0f, 1.3f * em)
|
||||||
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
canvas.drawText(trimmedUnits, rect.centerX(), rect.centerY(), pUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
drawNotesIndicator(canvas, color, em, notes)
|
drawNotesIndicator(canvas, color, em, notes)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.widget.FrameLayout
|
|||||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitPresenter
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitState
|
import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitBinding
|
import org.isoron.uhabits.databinding.ShowHabitBinding
|
||||||
|
import org.isoron.uhabits.utils.applyBottomInset
|
||||||
import org.isoron.uhabits.utils.applyToolbarInsets
|
import org.isoron.uhabits.utils.applyToolbarInsets
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ class ShowHabitView(context: Context) : FrameLayout(context) {
|
|||||||
} else {
|
} else {
|
||||||
binding.targetCard.visibility = GONE
|
binding.targetCard.visibility = GONE
|
||||||
}
|
}
|
||||||
|
binding.linearLayout.applyBottomInset()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setListener(presenter: ShowHabitPresenter) {
|
fun setListener(presenter: ShowHabitPresenter) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.isoron.uhabits.R
|
|||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.core.models.PaletteColor
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.databinding.SettingsActivityBinding
|
import org.isoron.uhabits.databinding.SettingsActivityBinding
|
||||||
|
import org.isoron.uhabits.utils.applyBottomInset
|
||||||
import org.isoron.uhabits.utils.applyRootViewInsets
|
import org.isoron.uhabits.utils.applyRootViewInsets
|
||||||
import org.isoron.uhabits.utils.setupToolbar
|
import org.isoron.uhabits.utils.setupToolbar
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
theme = themeSwitcher.currentTheme
|
theme = themeSwitcher.currentTheme
|
||||||
)
|
)
|
||||||
binding.root.applyRootViewInsets()
|
binding.root.applyRootViewInsets()
|
||||||
|
binding.root.applyBottomInset()
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,6 +260,14 @@ fun View.applyRootViewInsets() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun View.applyBottomInset() {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
|
||||||
|
val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
view.setPadding(0, 0, 0, systemBarsInsets.bottom)
|
||||||
|
insets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun View.applyToolbarInsets() {
|
fun View.applyToolbarInsets() {
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
|
||||||
val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
~
|
~
|
||||||
~ This file is part of Loop Habit Tracker.
|
~ This file is part of Loop Habit Tracker.
|
||||||
@@ -28,7 +27,7 @@
|
|||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
app:popupTheme="?toolbarPopupTheme"
|
app:popupTheme="?toolbarPopupTheme"
|
||||||
style="@style/Toolbar"/>
|
style="@style/Toolbar" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/scrollView"
|
android:id="@+id/scrollView"
|
||||||
@@ -37,6 +36,7 @@
|
|||||||
android:layout_below="@id/toolbar">
|
android:layout_below="@id/toolbar">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/outerLinearLayout"
|
||||||
style="@style/CardList">
|
style="@style/CardList">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
android:layout_width="100dp"
|
android:layout_width="100dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
android:layout_margin="6dp"
|
android:layout_margin="6dp"
|
||||||
android:src="@drawable/intro_icon_1"/>
|
android:src="@drawable/intro_icon_1" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@@ -57,14 +57,14 @@
|
|||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:layout_margin="6dp"
|
android:layout_margin="6dp"
|
||||||
android:textColor="?aboutScreenColor"
|
android:textColor="?aboutScreenColor"
|
||||||
android:text="@string/app_name"/>
|
android:text="@string/app_name" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvVersion"
|
android:id="@+id/tvVersion"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text=""/>
|
android:text="" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -75,32 +75,32 @@
|
|||||||
<TextView
|
<TextView
|
||||||
style="@style/CardHeader"
|
style="@style/CardHeader"
|
||||||
android:text="@string/links"
|
android:text="@string/links"
|
||||||
android:textColor="?aboutScreenColor"/>
|
android:textColor="?aboutScreenColor" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvRate"
|
android:id="@+id/tvRate"
|
||||||
style="@style/About.Item.Clickable"
|
style="@style/About.Item.Clickable"
|
||||||
android:text="@string/pref_rate_this_app"/>
|
android:text="@string/pref_rate_this_app" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvFeedback"
|
android:id="@+id/tvFeedback"
|
||||||
style="@style/About.Item.Clickable"
|
style="@style/About.Item.Clickable"
|
||||||
android:text="@string/pref_send_feedback"/>
|
android:text="@string/pref_send_feedback" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvTranslate"
|
android:id="@+id/tvTranslate"
|
||||||
style="@style/About.Item.Clickable"
|
style="@style/About.Item.Clickable"
|
||||||
android:text="@string/help_translate"/>
|
android:text="@string/help_translate" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvSource"
|
android:id="@+id/tvSource"
|
||||||
style="@style/About.Item.Clickable"
|
style="@style/About.Item.Clickable"
|
||||||
android:text="@string/pref_view_source_code"/>
|
android:text="@string/pref_view_source_code" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvPrivacy"
|
android:id="@+id/tvPrivacy"
|
||||||
style="@style/About.Item.Clickable"
|
style="@style/About.Item.Clickable"
|
||||||
android:text="@string/pref_view_privacy"/>
|
android:text="@string/pref_view_privacy" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
style="@style/CardHeader"
|
style="@style/CardHeader"
|
||||||
android:text="@string/developers"
|
android:text="@string/developers"
|
||||||
android:textColor="?aboutScreenColor"/>
|
android:textColor="?aboutScreenColor" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
This file lists developers who contributed with at least 100 lines
|
This file lists developers who contributed with at least 100 lines
|
||||||
@@ -120,31 +120,76 @@
|
|||||||
linked at the bottom of the list. If you qualify, please feel free to
|
linked at the bottom of the list. If you qualify, please feel free to
|
||||||
submit a pull request adding yourself here.
|
submit a pull request adding yourself here.
|
||||||
-->
|
-->
|
||||||
<TextView style="@style/About.Item" android:text="Álinson S. Xavier (@iSoron)"/>
|
<TextView
|
||||||
<TextView style="@style/About.Item" android:text="Quentin Hibon (@hiqua)"/>
|
style="@style/About.Item"
|
||||||
<TextView style="@style/About.Item" android:text="Oleg Ivashchenko (@olegivo)"/>
|
android:text="Álinson S. Xavier (@iSoron)" />
|
||||||
<TextView style="@style/About.Item" android:text="Kristian Tashkov (@KristianTashkov)"/>
|
|
||||||
<TextView style="@style/About.Item" android:text="Jakub Kalinowski (@kalina559)"/>
|
<TextView
|
||||||
<TextView style="@style/About.Item" android:text="Rechee Jozil (@recheej)"/>
|
style="@style/About.Item"
|
||||||
<TextView style="@style/About.Item" android:text="Sebastian Gallese (@sgallese)"/>
|
android:text="Quentin Hibon (@hiqua)" />
|
||||||
<TextView style="@style/About.Item" android:text="Luboš Luňák (@llunak)"/>
|
|
||||||
<TextView style="@style/About.Item" android:text="Bindu (@vbh)"/>
|
<TextView
|
||||||
<TextView style="@style/About.Item" android:text="Victor Yu (@vyu1)"/>
|
style="@style/About.Item"
|
||||||
<TextView style="@style/About.Item" android:text="Christoph Hennemann (@chennemann)"/>
|
android:text="Oleg Ivashchenko (@olegivo)" />
|
||||||
<TextView style="@style/About.Item" android:text="Денис (@sciamano)"/>
|
|
||||||
<TextView style="@style/About.Item" android:text="Joseph Tran (@JotraN)"/>
|
<TextView
|
||||||
<TextView style="@style/About.Item" android:text="Nikhil (@regularcoder)"/>
|
style="@style/About.Item"
|
||||||
<TextView style="@style/About.Item" android:text="JanetQC"/>
|
android:text="Kristian Tashkov (@KristianTashkov)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Jakub Kalinowski (@kalina559)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Rechee Jozil (@recheej)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Sebastian Gallese (@sgallese)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Luboš Luňák (@llunak)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Bindu (@vbh)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Victor Yu (@vyu1)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Christoph Hennemann (@chennemann)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Денис (@sciamano)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Joseph Tran (@JotraN)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="Nikhil (@regularcoder)" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/About.Item"
|
||||||
|
android:text="JanetQC" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvContributors"
|
android:id="@+id/tvContributors"
|
||||||
style="@style/About.Item.Clickable"
|
style="@style/About.Item.Clickable"
|
||||||
android:text="@string/view_all_contributors"/>
|
android:text="@string/view_all_contributors" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<include layout="@layout/about_translators"/>
|
<include layout="@layout/about_translators" />
|
||||||
|
|
||||||
>
|
>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout android:id="@+id/container"
|
||||||
android:id="@+id/container"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -29,7 +28,7 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
style="@style/Toolbar"
|
style="@style/Toolbar"
|
||||||
app:popupTheme="?toolbarPopupTheme"
|
app:popupTheme="?toolbarPopupTheme"
|
||||||
android:layout_alignParentTop="true"/>
|
android:layout_alignParentTop="true" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/scrollView"
|
android:id="@+id/scrollView"
|
||||||
@@ -41,11 +40,12 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
style="@style/CardList"
|
style="@style/CardList"
|
||||||
|
android:id="@+id/linearLayout"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.SubtitleCardView
|
<org.isoron.uhabits.activities.habits.show.views.SubtitleCardView
|
||||||
android:id="@+id/subtitleCard"
|
android:id="@+id/subtitleCard"
|
||||||
style="@style/ShowHabit.Subtitle"/>
|
style="@style/ShowHabit.Subtitle" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.NotesCardView
|
<org.isoron.uhabits.activities.habits.show.views.NotesCardView
|
||||||
android:id="@+id/notesCard"
|
android:id="@+id/notesCard"
|
||||||
@@ -55,36 +55,36 @@
|
|||||||
<org.isoron.uhabits.activities.habits.show.views.OverviewCardView
|
<org.isoron.uhabits.activities.habits.show.views.OverviewCardView
|
||||||
android:id="@+id/overviewCard"
|
android:id="@+id/overviewCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:paddingTop="12dp"/>
|
android:paddingTop="12dp" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.TargetCardView
|
<org.isoron.uhabits.activities.habits.show.views.TargetCardView
|
||||||
android:id="@+id/targetCard"
|
android:id="@+id/targetCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:paddingTop="12dp"/>
|
android:paddingTop="12dp" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.ScoreCardView
|
<org.isoron.uhabits.activities.habits.show.views.ScoreCardView
|
||||||
android:id="@+id/scoreCard"
|
android:id="@+id/scoreCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:gravity="center"/>
|
android:gravity="center" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.BarCardView
|
<org.isoron.uhabits.activities.habits.show.views.BarCardView
|
||||||
android:id="@+id/barCard"
|
android:id="@+id/barCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:gravity="center"/>
|
android:gravity="center" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.HistoryCardView
|
<org.isoron.uhabits.activities.habits.show.views.HistoryCardView
|
||||||
android:id="@+id/historyCard"
|
android:id="@+id/historyCard"
|
||||||
style="@style/Card"
|
style="@style/Card"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:paddingBottom="0dp"/>
|
android:paddingBottom="0dp" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.StreakCardView
|
<org.isoron.uhabits.activities.habits.show.views.StreakCardView
|
||||||
android:id="@+id/streakCard"
|
android:id="@+id/streakCard"
|
||||||
style="@style/Card"/>
|
style="@style/Card" />
|
||||||
|
|
||||||
<org.isoron.uhabits.activities.habits.show.views.FrequencyCardView
|
<org.isoron.uhabits.activities.habits.show.views.FrequencyCardView
|
||||||
android:id="@+id/frequencyCard"
|
android:id="@+id/frequencyCard"
|
||||||
style="@style/Card"/>
|
style="@style/Card" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
~ Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
|
||||||
~
|
~
|
||||||
~ This file is part of Loop Habit Tracker.
|
~ This file is part of Loop Habit Tracker.
|
||||||
@@ -32,8 +31,7 @@
|
|||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:textColor="?attr/contrast60"
|
android:textColor="?attr/contrast60"
|
||||||
android:textSize="@dimen/regularTextSize"
|
android:textSize="@dimen/regularTextSize"
|
||||||
tools:text="Have you worked out today?"
|
tools:text="Have you worked out today?" />
|
||||||
/>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -54,12 +52,14 @@
|
|||||||
android:id="@+id/targetText"
|
android:id="@+id/targetText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="8.5k"
|
android:text=""
|
||||||
android:textColor="?attr/contrast60"
|
android:textColor="?attr/contrast60"
|
||||||
android:textSize="@dimen/smallTextSize"
|
android:textSize="@dimen/smallTextSize"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
/>
|
android:maxEms="7"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/frequencyIcon"
|
android:id="@+id/frequencyIcon"
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="1dp"
|
android:paddingTop="1dp"
|
||||||
android:textColor="?attr/contrast60"
|
android:textColor="?attr/contrast60"
|
||||||
android:text="8:00 AM"
|
android:text=""
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:textSize="@dimen/smallTextSize" />
|
android:textSize="@dimen/smallTextSize" />
|
||||||
|
|
||||||
|
|||||||
@@ -126,8 +126,9 @@
|
|||||||
<item name="highlightedBackgroundColor">@color/black</item>
|
<item name="highlightedBackgroundColor">@color/black</item>
|
||||||
<item name="contrast0">@color/black</item>
|
<item name="contrast0">@color/black</item>
|
||||||
<item name="contrast20">@color/grey_900</item>
|
<item name="contrast20">@color/grey_900</item>
|
||||||
<item name="contrast60">@color/grey_700</item>
|
<item name="contrast40">@color/grey_800</item>
|
||||||
<item name="contrast80">@color/grey_700</item>
|
<item name="contrast60">@color/grey_500</item>
|
||||||
|
<item name="contrast80">@color/grey_400</item>
|
||||||
<item name="contrast100">@color/grey_200</item>
|
<item name="contrast100">@color/grey_200</item>
|
||||||
<item name="selectedBackground">@drawable/selected_box</item>
|
<item name="selectedBackground">@drawable/selected_box</item>
|
||||||
<item name="textColorAlertDialogListItem">@color/grey_100</item>
|
<item name="textColorAlertDialogListItem">@color/grey_100</item>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Date,Value,Notes
|
||||||
|
|||||||
|
@@ -1 +1,2 @@
|
|||||||
|
Date,Score
|
||||||
2015-01-25,0.0000
|
2015-01-25,0.0000
|
||||||
|
|||||||
|
@@ -1,10 +1,11 @@
|
|||||||
2015-01-25,2
|
Date,Value,Notes
|
||||||
2015-01-24,0
|
2015-01-25,YES_MANUAL,
|
||||||
2015-01-23,1
|
2015-01-24,NO,Sick
|
||||||
2015-01-22,2
|
2015-01-23,YES_AUTO,"Forgot to do it, really"
|
||||||
2015-01-21,2
|
2015-01-22,YES_MANUAL,
|
||||||
2015-01-20,2
|
2015-01-21,YES_MANUAL,
|
||||||
2015-01-19,1
|
2015-01-20,YES_MANUAL,
|
||||||
2015-01-18,1
|
2015-01-19,YES_AUTO,"""Vacation"""
|
||||||
2015-01-17,2
|
2015-01-18,YES_AUTO,
|
||||||
2015-01-16,2
|
2015-01-17,YES_MANUAL,
|
||||||
|
2015-01-16,YES_MANUAL,
|
||||||
|
|||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
Date,Score
|
||||||
2015-01-25,0.2557
|
2015-01-25,0.2557
|
||||||
2015-01-24,0.2226
|
2015-01-24,0.2226
|
||||||
2015-01-23,0.1991
|
2015-01-23,0.1991
|
||||||
|
|||||||
|
@@ -1,11 +1,11 @@
|
|||||||
Date,Meditate,Wake up early,
|
Date,Meditate,Wake up early,
|
||||||
2015-01-25,-1,2,
|
2015-01-25,UNKNOWN,YES_MANUAL,
|
||||||
2015-01-24,-1,0,
|
2015-01-24,UNKNOWN,NO,
|
||||||
2015-01-23,-1,1,
|
2015-01-23,UNKNOWN,YES_AUTO,
|
||||||
2015-01-22,-1,2,
|
2015-01-22,UNKNOWN,YES_MANUAL,
|
||||||
2015-01-21,-1,2,
|
2015-01-21,UNKNOWN,YES_MANUAL,
|
||||||
2015-01-20,-1,2,
|
2015-01-20,UNKNOWN,YES_MANUAL,
|
||||||
2015-01-19,-1,1,
|
2015-01-19,UNKNOWN,YES_AUTO,
|
||||||
2015-01-18,-1,1,
|
2015-01-18,UNKNOWN,YES_AUTO,
|
||||||
2015-01-17,-1,2,
|
2015-01-17,UNKNOWN,YES_MANUAL,
|
||||||
2015-01-16,-1,2,
|
2015-01-16,UNKNOWN,YES_MANUAL,
|
||||||
|
|||||||
|
@@ -1,3 +1,3 @@
|
|||||||
Position,Name,Question,Description,NumRepetitions,Interval,Color
|
Position,Name,Type,Question,Description,FrequencyNumerator,FrequencyDenominator,Color,Unit,Target Type,Target Value,Archived?
|
||||||
001,Meditate,Did you meditate this morning?,,1,1,#FF8F00
|
001,Meditate,YES_NO,Did you meditate this morning?,,1,1,#FF8F00,,,,false
|
||||||
002,Wake up early,Did you wake up before 6am?,,2,3,#00897B
|
002,Wake up early,YES_NO,Did you wake up before 6am?,,2,3,#00897B,,,,false
|
||||||
|
|||||||
|
@@ -24,7 +24,7 @@ plugins {
|
|||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm().withJava()
|
jvm().withJava()
|
||||||
jvmToolchain(11)
|
jvmToolchain(17)
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.core.io
|
package org.isoron.uhabits.core.io
|
||||||
|
|
||||||
|
import com.opencsv.CSVWriter
|
||||||
import org.isoron.uhabits.core.models.Entry
|
import org.isoron.uhabits.core.models.Entry
|
||||||
import org.isoron.uhabits.core.models.EntryList
|
import org.isoron.uhabits.core.models.EntryList
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -32,7 +33,6 @@ import java.io.FileOutputStream
|
|||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Writer
|
import java.io.Writer
|
||||||
import java.util.ArrayList
|
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
@@ -109,11 +109,14 @@ class HabitsCSVExporter(
|
|||||||
var oldest = today
|
var oldest = today
|
||||||
val known = habit.computedEntries.getKnown()
|
val known = habit.computedEntries.getKnown()
|
||||||
if (known.isNotEmpty()) oldest = known[known.size - 1].timestamp
|
if (known.isNotEmpty()) oldest = known[known.size - 1].timestamp
|
||||||
|
val csv = CSVWriter(out)
|
||||||
|
csv.writeNext(arrayOf("Date", "Score"), false)
|
||||||
for ((timestamp1, value) in habit.scores.getByInterval(oldest, today)) {
|
for ((timestamp1, value) in habit.scores.getByInterval(oldest, today)) {
|
||||||
val timestamp = dateFormat.format(timestamp1.unixTime)
|
val timestamp = dateFormat.format(timestamp1.unixTime)
|
||||||
val score = String.format(Locale.US, "%.4f", value)
|
val score = String.format(Locale.US, "%.4f", value)
|
||||||
out.write(String.format("%s,%s\n", timestamp, score))
|
csv.writeNext(arrayOf(timestamp, score), false)
|
||||||
}
|
}
|
||||||
|
csv.close()
|
||||||
out.close()
|
out.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,10 +125,20 @@ class HabitsCSVExporter(
|
|||||||
val out = FileWriter(exportDirName + filename)
|
val out = FileWriter(exportDirName + filename)
|
||||||
generatedFilenames.add(filename)
|
generatedFilenames.add(filename)
|
||||||
val dateFormat = DateFormats.getCSVDateFormat()
|
val dateFormat = DateFormats.getCSVDateFormat()
|
||||||
for ((timestamp, value) in entries.getKnown()) {
|
val csv = CSVWriter(out)
|
||||||
val date = dateFormat.format(timestamp.toJavaDate())
|
csv.writeNext(arrayOf("Date", "Value", "Notes"), false)
|
||||||
out.write(String.format(Locale.US, "%s,%d\n", date, value))
|
for (entry in entries.getKnown()) {
|
||||||
|
val date = dateFormat.format(entry.timestamp.toJavaDate())
|
||||||
|
csv.writeNext(
|
||||||
|
arrayOf(
|
||||||
|
date,
|
||||||
|
entry.formattedValue,
|
||||||
|
entry.notes
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
csv.close()
|
||||||
out.close()
|
out.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +180,7 @@ class HabitsCSVExporter(
|
|||||||
checksWriter.write(sb.toString())
|
checksWriter.write(sb.toString())
|
||||||
scoresWriter.write(sb.toString())
|
scoresWriter.write(sb.toString())
|
||||||
for (j in selectedHabits.indices) {
|
for (j in selectedHabits.indices) {
|
||||||
checksWriter.write(checkmarks[j][i].value.toString())
|
checksWriter.write(checkmarks[j][i].formattedValue)
|
||||||
checksWriter.write(delimiter)
|
checksWriter.write(delimiter)
|
||||||
val score = String.format(Locale.US, "%.4f", scores[j][i].value)
|
val score = String.format(Locale.US, "%.4f", scores[j][i].value)
|
||||||
scoresWriter.write(score)
|
scoresWriter.write(score)
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ data class Entry(
|
|||||||
val value: Int,
|
val value: Int,
|
||||||
val notes: String = ""
|
val notes: String = ""
|
||||||
) {
|
) {
|
||||||
|
val formattedValue: String
|
||||||
|
get() = when (value) {
|
||||||
|
YES_MANUAL -> "YES_MANUAL"
|
||||||
|
YES_AUTO -> "YES_AUTO"
|
||||||
|
NO -> "NO"
|
||||||
|
SKIP -> "SKIP"
|
||||||
|
UNKNOWN -> "UNKNOWN"
|
||||||
|
else -> value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Value indicating that the habit is not applicable for this timestamp.
|
* Value indicating that the habit is not applicable for this timestamp.
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.opencsv.CSVWriter
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Writer
|
import java.io.Writer
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
import java.util.Locale
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,24 +183,34 @@ abstract class HabitList : Iterable<Habit> {
|
|||||||
val header = arrayOf(
|
val header = arrayOf(
|
||||||
"Position",
|
"Position",
|
||||||
"Name",
|
"Name",
|
||||||
|
"Type",
|
||||||
"Question",
|
"Question",
|
||||||
"Description",
|
"Description",
|
||||||
"NumRepetitions",
|
"FrequencyNumerator",
|
||||||
"Interval",
|
"FrequencyDenominator",
|
||||||
"Color"
|
"Color",
|
||||||
|
"Unit",
|
||||||
|
"Target Type",
|
||||||
|
"Target Value",
|
||||||
|
"Archived?"
|
||||||
)
|
)
|
||||||
val csv = CSVWriter(out)
|
val csv = CSVWriter(out)
|
||||||
csv.writeNext(header, false)
|
csv.writeNext(header, false)
|
||||||
for (habit in this) {
|
for (habit in this) {
|
||||||
val (numerator, denominator) = habit.frequency
|
val (numerator, denominator) = habit.frequency
|
||||||
val cols = arrayOf(
|
val cols = arrayOf(
|
||||||
String.format("%03d", indexOf(habit) + 1),
|
String.format(Locale.US, "%03d", indexOf(habit) + 1),
|
||||||
habit.name,
|
habit.name,
|
||||||
|
habit.type.name,
|
||||||
habit.question,
|
habit.question,
|
||||||
habit.description,
|
habit.description,
|
||||||
numerator.toString(),
|
numerator.toString(),
|
||||||
denominator.toString(),
|
denominator.toString(),
|
||||||
habit.color.toCsvColor()
|
habit.color.toCsvColor(),
|
||||||
|
if (habit.isNumerical) habit.unit else "",
|
||||||
|
if (habit.isNumerical) habit.targetType.name else "",
|
||||||
|
if (habit.isNumerical) habit.targetValue.toString() else "",
|
||||||
|
habit.isArchived.toString()
|
||||||
)
|
)
|
||||||
csv.writeNext(cols, false)
|
csv.writeNext(cols, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ class HabitFixtures(private val modelFactory: ModelFactory, private val habitLis
|
|||||||
true, false, false, true, true, true, false, false, true, true
|
true, false, false, true, true, true, false, false, true, true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private var NON_DAILY_HABIT_NOTES = arrayOf(
|
||||||
|
"", "Sick", "Forgot to do it, really", "", "", "", "\"Vacation\"", "", "", ""
|
||||||
|
)
|
||||||
|
|
||||||
fun createEmptyHabit(
|
fun createEmptyHabit(
|
||||||
name: String = "Meditate",
|
name: String = "Meditate",
|
||||||
color: PaletteColor = PaletteColor(3),
|
color: PaletteColor = PaletteColor(3),
|
||||||
@@ -141,10 +145,10 @@ class HabitFixtures(private val modelFactory: ModelFactory, private val habitLis
|
|||||||
habit.frequency = Frequency(2, 3)
|
habit.frequency = Frequency(2, 3)
|
||||||
saveIfSQLite(habit)
|
saveIfSQLite(habit)
|
||||||
var timestamp = getToday()
|
var timestamp = getToday()
|
||||||
for (c in NON_DAILY_HABIT_CHECKS) {
|
for (i in NON_DAILY_HABIT_CHECKS.indices) {
|
||||||
var value = Entry.NO
|
var value = Entry.NO
|
||||||
if (c) value = Entry.YES_MANUAL
|
if (NON_DAILY_HABIT_CHECKS[i]) value = Entry.YES_MANUAL
|
||||||
habit.originalEntries.add(Entry(timestamp, value))
|
habit.originalEntries.add(Entry(timestamp, value, NON_DAILY_HABIT_NOTES[i]))
|
||||||
timestamp = timestamp.minus(1)
|
timestamp = timestamp.minus(1)
|
||||||
}
|
}
|
||||||
habit.recompute()
|
habit.recompute()
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.isoron.uhabits.core.io
|
package org.isoron.uhabits.core.io
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils
|
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
import org.isoron.uhabits.core.BaseUnitTest
|
import org.isoron.uhabits.core.BaseUnitTest
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -30,6 +29,7 @@ import java.io.IOException
|
|||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class HabitsCSVExporterTest : BaseUnitTest() {
|
class HabitsCSVExporterTest : BaseUnitTest() {
|
||||||
@@ -108,15 +108,12 @@ class HabitsCSVExporterTest : BaseUnitTest() {
|
|||||||
|
|
||||||
private fun assertFileAndReferenceAreEqual(s: String) {
|
private fun assertFileAndReferenceAreEqual(s: String) {
|
||||||
val assetFilename = String.format("csv_export/%s", s)
|
val assetFilename = String.format("csv_export/%s", s)
|
||||||
val file = File.createTempFile("asset", "")
|
val actualFile = File(String.format("%s/%s", baseDir.absolutePath, s))
|
||||||
file.deleteOnExit()
|
val expectedFile = File.createTempFile("asset", "")
|
||||||
copyAssetToFile(assetFilename, file)
|
expectedFile.deleteOnExit()
|
||||||
|
copyAssetToFile(assetFilename, expectedFile)
|
||||||
assertTrue(
|
val actualContents = actualFile.readText()
|
||||||
FileUtils.contentEquals(
|
val expectedContents = expectedFile.readText()
|
||||||
file,
|
assertEquals(expectedContents, actualContents, "content mismatch for $s")
|
||||||
File(String.format("%s/%s", baseDir.absolutePath, s))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,13 +202,16 @@ class HabitListTest : BaseUnitTest() {
|
|||||||
h2.description = ""
|
h2.description = ""
|
||||||
h2.frequency = Frequency(2, 3)
|
h2.frequency = Frequency(2, 3)
|
||||||
h2.color = PaletteColor(5)
|
h2.color = PaletteColor(5)
|
||||||
|
val h3 = fixtures.createNumericalHabit()
|
||||||
list.add(h1)
|
list.add(h1)
|
||||||
list.add(h2)
|
list.add(h2)
|
||||||
|
list.add(h3)
|
||||||
val expectedCSV =
|
val expectedCSV =
|
||||||
"""
|
"""
|
||||||
Position,Name,Question,Description,NumRepetitions,Interval,Color
|
Position,Name,Type,Question,Description,FrequencyNumerator,FrequencyDenominator,Color,Unit,Target Type,Target Value,Archived?
|
||||||
001,Meditate,Did you meditate this morning?,this is a test description,1,1,#FF8F00
|
001,Meditate,YES_NO,Did you meditate this morning?,this is a test description,1,1,#FF8F00,,,,false
|
||||||
002,Wake up early,Did you wake up before 6am?,,2,3,#AFB42B
|
002,Run,NUMERICAL,How many miles did you run today?,,1,1,#E64A19,miles,AT_LEAST,2.0,false
|
||||||
|
003,Wake up early,YES_NO,Did you wake up before 6am?,,2,3,#AFB42B,,,,false
|
||||||
|
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
|
|||||||
Reference in New Issue
Block a user