Merge branch 'dev' into dev

pull/1743/head
Gokul K 2 years ago committed by GitHub
commit 83328b0a62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,11 +1,11 @@
plugins { plugins {
val kotlinVersion = "1.7.21" val kotlinVersion = "1.7.21"
id("com.android.application") version ("7.3.1") apply (false) id("com.android.application") version "7.4.2" apply (false)
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false) id("org.jetbrains.kotlin.android") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false) id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false) id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false) id("org.jetbrains.kotlin.multiplatform") version kotlinVersion apply (false)
id("org.jlleitschuh.gradle.ktlint") version "11.0.0" id("org.jlleitschuh.gradle.ktlint") version "11.4.2"
} }
apply { apply {

@ -19,7 +19,7 @@
plugins { plugins {
id("com.github.triplet.play") version "3.7.0" id("com.github.triplet.play") version "3.7.0"
id("com.android.application") id("com.android.application") version "7.4.2"
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.kapt") id("org.jetbrains.kotlin.kapt")
id("org.jetbrains.kotlin.android.extensions") id("org.jetbrains.kotlin.android.extensions")
@ -37,7 +37,7 @@ android {
defaultConfig { defaultConfig {
versionCode = 20200 versionCode = 20200
versionName = "2.2.0" versionName = "2.2.0"
minSdk = 23 minSdk = 28
targetSdk = 31 targetSdk = 31
applicationId = "org.isoron.uhabits" applicationId = "org.isoron.uhabits"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@ -96,7 +96,7 @@ dependencies {
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0") androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
androidTestImplementation("androidx.test:rules:1.5.0") androidTestImplementation("androidx.test:rules:1.5.0")
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") androidTestImplementation("org.mockito.kotlin:mockito-kotlin:2.2.11")
compileOnly("javax.annotation:jsr250-api:1.0") compileOnly("javax.annotation:jsr250-api:1.0")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2")
implementation("com.github.AppIntro:AppIntro:6.2.0") implementation("com.github.AppIntro:AppIntro:6.2.0")
@ -121,7 +121,7 @@ dependencies {
kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion") kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion")
testImplementation("com.google.dagger:dagger:$daggerVersion") testImplementation("com.google.dagger:dagger:$daggerVersion")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") testImplementation("org.mockito.kotlin:mockito-kotlin:2.2.11")
} }
kapt { kapt {

@ -39,6 +39,7 @@ open class BaseUserInterfaceTest {
private lateinit var prefs: Preferences private lateinit var prefs: Preferences
private lateinit var fixtures: HabitFixtures private lateinit var fixtures: HabitFixtures
private lateinit var cache: HabitCardListCache private lateinit var cache: HabitCardListCache
@Before @Before
@Throws(Exception::class) @Throws(Exception::class)
fun setUp() { fun setUp() {

@ -153,18 +153,22 @@ open class BaseViewTest : BaseAndroidTest() {
var filename = filename var filename = filename
var dir = getSDCardDir("test-screenshots") var dir = getSDCardDir("test-screenshots")
if (dir == null) dir = AndroidDirFinder(targetContext).getFilesDir("test-screenshots") if (dir == null) dir = AndroidDirFinder(targetContext).getFilesDir("test-screenshots")
if (dir == null) throw RuntimeException( if (dir == null) {
"Could not find suitable dir for screenshots" throw RuntimeException(
) "Could not find suitable dir for screenshots"
)
}
filename = filename.replace("\\.png$".toRegex(), "$suffix.png") filename = filename.replace("\\.png$".toRegex(), "$suffix.png")
val absolutePath = String.format("%s/%s", dir.absolutePath, filename) val absolutePath = String.format("%s/%s", dir.absolutePath, filename)
val parent = File(absolutePath).parentFile val parent = File(absolutePath).parentFile
if (!parent.exists() && !parent.mkdirs()) throw RuntimeException( if (!parent.exists() && !parent.mkdirs()) {
String.format( throw RuntimeException(
"Could not create dir: %s", String.format(
parent.absolutePath "Could not create dir: %s",
parent.absolutePath
)
) )
) }
val out = FileOutputStream(absolutePath) val out = FileOutputStream(absolutePath)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out) bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
return absolutePath return absolutePath

@ -19,7 +19,6 @@
package org.isoron.uhabits package org.isoron.uhabits
import com.nhaarman.mockitokotlin2.mock
import dagger.Component import dagger.Component
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -35,6 +34,7 @@ import org.isoron.uhabits.inject.ActivityScope
import org.isoron.uhabits.inject.HabitModule import org.isoron.uhabits.inject.HabitModule
import org.isoron.uhabits.inject.HabitsActivityModule import org.isoron.uhabits.inject.HabitsActivityModule
import org.isoron.uhabits.inject.HabitsApplicationComponent import org.isoron.uhabits.inject.HabitsApplicationComponent
import org.mockito.kotlin.mock
@Module @Module
class TestModule { class TestModule {

@ -44,7 +44,7 @@ class ScoreChartTest : BaseViewTest() {
habit = habit, habit = habit,
firstWeekday = prefs.firstWeekdayInt, firstWeekday = prefs.firstWeekdayInt,
spinnerPosition = 0, spinnerPosition = 0,
theme = LightTheme(), theme = LightTheme()
) )
view = ScoreChart(targetContext).apply { view = ScoreChart(targetContext).apply {
setScores(state.scores) setScores(state.scores)

@ -30,6 +30,7 @@ import org.junit.runner.RunWith
@MediumTest @MediumTest
class StreakChartTest : BaseViewTest() { class StreakChartTest : BaseViewTest() {
private lateinit var view: StreakChart private lateinit var view: StreakChart
@Before @Before
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()

@ -20,15 +20,15 @@ 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 com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import com.nhaarman.mockitokotlin2.whenever
import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.BaseViewTest
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@MediumTest @MediumTest

@ -20,9 +20,6 @@ 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 com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.BaseViewTest import org.isoron.uhabits.BaseViewTest
@ -30,6 +27,9 @@ import org.isoron.uhabits.core.ui.screens.habits.list.HintList
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@MediumTest @MediumTest

@ -48,7 +48,7 @@ class FrequencyCardViewTest : BaseViewTest() {
FrequencyCardPresenter.buildState( FrequencyCardPresenter.buildState(
habit = habit, habit = habit,
firstWeekday = 0, firstWeekday = 0,
theme = LightTheme(), theme = LightTheme()
) )
) )
measureView(view, 800f, 600f) measureView(view, 800f, 600f)

@ -49,7 +49,7 @@ class HistoryCardViewTest : BaseViewTest() {
HistoryCardPresenter.buildState( HistoryCardPresenter.buildState(
habit = habit, habit = habit,
firstWeekday = SUNDAY, firstWeekday = SUNDAY,
theme = LightTheme(), theme = LightTheme()
) )
) )
measureView(view, 800f, 600f) measureView(view, 800f, 600f)

@ -51,7 +51,7 @@ class OverviewCardViewTest : BaseViewTest() {
scoreYearDiff = 0.74f, scoreYearDiff = 0.74f,
totalCount = 44, totalCount = 44,
color = PaletteColor(7), color = PaletteColor(7),
theme = LightTheme(), theme = LightTheme()
) )
) )
measureView(view, 800f, 300f) measureView(view, 800f, 300f)

@ -49,7 +49,7 @@ class ScoreCardViewTest : BaseViewTest() {
habit = habit, habit = habit,
firstWeekday = 0, firstWeekday = 0,
spinnerPosition = 0, spinnerPosition = 0,
theme = LightTheme(), theme = LightTheme()
) )
) )
measureView(view, 800f, 600f) measureView(view, 800f, 600f)

@ -48,7 +48,7 @@ class StreakCardViewTest : BaseViewTest() {
StreakCardState( StreakCardState(
bestStreaks = habit.streaks.getBest(10), bestStreaks = habit.streaks.getBest(10),
color = habit.color, color = habit.color,
theme = LightTheme(), theme = LightTheme()
) )
) )
measureView(view, 800f, 600f) measureView(view, 800f, 600f)

@ -53,7 +53,7 @@ class SubtitleCardViewTest : BaseViewTest() {
isNumerical = false, isNumerical = false,
question = "Did you meditate this morning?", question = "Did you meditate this morning?",
reminder = Reminder(8, 30, EVERY_DAY), reminder = Reminder(8, 30, EVERY_DAY),
theme = LightTheme(), theme = LightTheme()
) )
) )
measureView(view, 800f, 200f) measureView(view, 800f, 200f)

@ -119,7 +119,7 @@ class ListHabitsRegressionTest : BaseUserInterfaceTest() {
"Wake up early", "Wake up early",
"Meditate", "Meditate",
"Read books", "Read books",
"Track time", "Track time"
) )
} }
} }

@ -33,6 +33,7 @@ import java.io.IOException
@MediumTest @MediumTest
class CheckmarkWidgetViewTest : BaseViewTest() { class CheckmarkWidgetViewTest : BaseViewTest() {
private lateinit var view: CheckmarkWidgetView private lateinit var view: CheckmarkWidgetView
@Before @Before
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()

@ -60,7 +60,7 @@ class AndroidCanvas : Canvas {
y1.toDp(), y1.toDp(),
x2.toDp(), x2.toDp(),
y2.toDp(), y2.toDp(),
paint, paint
) )
} }
@ -69,7 +69,7 @@ class AndroidCanvas : Canvas {
text, text,
x.toDp(), x.toDp(),
y.toDp() + 0.6f * mHeight, y.toDp() + 0.6f * mHeight,
textPaint, textPaint
) )
} }
@ -83,7 +83,7 @@ class AndroidCanvas : Canvas {
y: Double, y: Double,
width: Double, width: Double,
height: Double, height: Double,
cornerRadius: Double, cornerRadius: Double
) { ) {
paint.style = Paint.Style.FILL paint.style = Paint.Style.FILL
innerCanvas.drawRoundRect( innerCanvas.drawRoundRect(
@ -93,7 +93,7 @@ class AndroidCanvas : Canvas {
(y + height).toDp(), (y + height).toDp(),
cornerRadius.toDp(), cornerRadius.toDp(),
cornerRadius.toDp(), cornerRadius.toDp(),
paint, paint
) )
} }
@ -108,7 +108,7 @@ class AndroidCanvas : Canvas {
y.toDp(), y.toDp(),
(x + width).toDp(), (x + width).toDp(),
(y + height).toDp(), (y + height).toDp(),
paint, paint
) )
} }
@ -148,7 +148,7 @@ class AndroidCanvas : Canvas {
centerY: Double, centerY: Double,
radius: Double, radius: Double,
startAngle: Double, startAngle: Double,
swipeAngle: Double, swipeAngle: Double
) { ) {
paint.style = Paint.Style.FILL paint.style = Paint.Style.FILL
innerCanvas.drawArc( innerCanvas.drawArc(
@ -159,14 +159,14 @@ class AndroidCanvas : Canvas {
-startAngle.toFloat(), -startAngle.toFloat(),
-swipeAngle.toFloat(), -swipeAngle.toFloat(),
true, true,
paint, paint
) )
} }
override fun fillCircle( override fun fillCircle(
centerX: Double, centerX: Double,
centerY: Double, centerY: Double,
radius: Double, radius: Double
) { ) {
paint.style = Paint.Style.FILL paint.style = Paint.Style.FILL
innerCanvas.drawCircle(centerX.toDp(), centerY.toDp(), radius.toDp(), paint) innerCanvas.drawCircle(centerX.toDp(), centerY.toDp(), radius.toDp(), paint)

@ -33,7 +33,7 @@ import kotlin.math.max
*/ */
class AndroidDataView( class AndroidDataView(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null
) : AndroidView<DataView>(context, attrs), ) : AndroidView<DataView>(context, attrs),
GestureDetector.OnGestureListener, GestureDetector.OnGestureListener,
ValueAnimator.AnimatorUpdateListener { ValueAnimator.AnimatorUpdateListener {
@ -60,7 +60,7 @@ class AndroidDataView(
e1: MotionEvent?, e1: MotionEvent?,
e2: MotionEvent?, e2: MotionEvent?,
dx: Float, dx: Float,
dy: Float, dy: Float
): Boolean { ): Boolean {
if (abs(dx) > abs(dy)) { if (abs(dx) > abs(dy)) {
val parent = parent val parent = parent
@ -82,7 +82,7 @@ class AndroidDataView(
e1: MotionEvent?, e1: MotionEvent?,
e2: MotionEvent?, e2: MotionEvent?,
velocityX: Float, velocityX: Float,
velocityY: Float, velocityY: Float
): Boolean { ): Boolean {
scroller.fling( scroller.fling(
scroller.currX, scroller.currX,
@ -140,8 +140,11 @@ class AndroidDataView(
// e.getPointerId. // e.getPointerId.
return false return false
} }
if (isSingleTap) view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity) if (isSingleTap) {
else view?.onLongClick(x / canvas.innerDensity, y / canvas.innerDensity) view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity)
} else {
view?.onLongClick(x / canvas.innerDensity, y / canvas.innerDensity)
}
return true return true
} }
} }

@ -47,6 +47,6 @@ fun Color.toInt(): Int {
(255 * this.alpha).roundToInt(), (255 * this.alpha).roundToInt(),
(255 * this.red).roundToInt(), (255 * this.red).roundToInt(),
(255 * this.green).roundToInt(), (255 * this.green).roundToInt(),
(255 * this.blue).roundToInt(), (255 * this.blue).roundToInt()
) )
} }

@ -24,7 +24,7 @@ import android.util.AttributeSet
open class AndroidView<T : View>( open class AndroidView<T : View>(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null
) : android.view.View(context, attrs) { ) : android.view.View(context, attrs) {
var view: T? = null var view: T? = null

@ -39,7 +39,7 @@ import org.isoron.uhabits.inject.ActivityScope
class AndroidThemeSwitcher class AndroidThemeSwitcher
constructor( constructor(
@ActivityContext val context: Context, @ActivityContext val context: Context,
preferences: Preferences, preferences: Preferences
) : ThemeSwitcher(preferences) { ) : ThemeSwitcher(preferences) {
override var currentTheme: Theme = LightTheme() override var currentTheme: Theme = LightTheme()

@ -27,7 +27,7 @@ import org.isoron.uhabits.utils.startActivitySafely
class AboutScreen( class AboutScreen(
private val activity: AboutActivity, private val activity: AboutActivity,
private val intents: IntentFactory, private val intents: IntentFactory,
private val prefs: Preferences, private val prefs: Preferences
) { ) {
private var developerCountdown = 5 private var developerCountdown = 5

@ -32,7 +32,7 @@ import org.isoron.uhabits.utils.setupToolbar
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class AboutView( class AboutView(
context: Context, context: Context,
private val screen: AboutScreen, private val screen: AboutScreen
) : FrameLayout(context) { ) : FrameLayout(context) {
private var binding = AboutBinding.inflate(LayoutInflater.from(context)) private var binding = AboutBinding.inflate(LayoutInflater.from(context))
@ -43,7 +43,7 @@ class AboutView(
toolbar = binding.toolbar, toolbar = binding.toolbar,
color = PaletteColor(11), color = PaletteColor(11),
title = resources.getString(R.string.about), title = resources.getString(R.string.about),
theme = currentTheme(), theme = currentTheme()
) )
val version = resources.getString(R.string.version_n) val version = resources.getString(R.string.version_n)
binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() } binding.tvContributors.setOnClickListener { screen.showCodeContributorsWebsite() }

@ -48,22 +48,22 @@ class FrequencyPickerDialog(
addBeforeAfterText( addBeforeAfterText(
this.getString(R.string.every_x_days), this.getString(R.string.every_x_days),
contentView.everyXDaysContainer, contentView.everyXDaysContainer
) )
addBeforeAfterText( addBeforeAfterText(
this.getString(R.string.x_times_per_week), this.getString(R.string.x_times_per_week),
contentView.xTimesPerWeekContainer, contentView.xTimesPerWeekContainer
) )
addBeforeAfterText( addBeforeAfterText(
this.getString(R.string.x_times_per_month), this.getString(R.string.x_times_per_month),
contentView.xTimesPerMonthContainer, contentView.xTimesPerMonthContainer
) )
addBeforeAfterText( addBeforeAfterText(
this.getString(R.string.x_times_per_y_days), this.getString(R.string.x_times_per_y_days),
contentView.xTimesPerYDaysContainer, contentView.xTimesPerYDaysContainer
) )
contentView.everyDayRadioButton.setOnClickListener { contentView.everyDayRadioButton.setOnClickListener {
@ -124,7 +124,8 @@ class FrequencyPickerDialog(
val parts = str.split("%d") val parts = str.split("%d")
for (i in parts.indices) { for (i in parts.indices) {
container.addView( container.addView(
TextView(activity).apply { text = parts[i].trim() }, 2 * i + 1, TextView(activity).apply { text = parts[i].trim() },
2 * i + 1
) )
} }
} }

@ -69,7 +69,7 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
theme = themeSwitcher.currentTheme, theme = themeSwitcher.currentTheme,
today = DateUtils.getTodayWithOffset().toLocalDate(), today = DateUtils.getTodayWithOffset().toLocalDate(),
onDateClickedListener = onDateClickedListener ?: object : OnDateClickedListener {}, onDateClickedListener = onDateClickedListener ?: object : OnDateClickedListener {},
padding = 10.0, padding = 10.0
) )
dataView = AndroidDataView(requireContext(), null) dataView = AndroidDataView(requireContext(), null)
dataView.view = chart!! dataView.view = chart!!

@ -181,8 +181,9 @@ class FrequencyChart : ScrollableChart {
rect[0f, 0f, baseSize.toFloat()] = baseSize.toFloat() rect[0f, 0f, baseSize.toFloat()] = baseSize.toFloat()
rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j) rect.offset(prevRect!!.left, prevRect!!.top + baseSize * j)
val i = localeWeekdayList[j] % 7 val i = localeWeekdayList[j] % 7
if (values != null) if (values != null) {
drawMarker(canvas, rect, values[i], weekDaysInMonth[i]) drawMarker(canvas, rect, values[i], weekDaysInMonth[i])
}
rect.offset(0f, rowHeight) rect.offset(0f, rowHeight)
} }
drawFooter(canvas, rect, date) drawFooter(canvas, rect, date)
@ -196,12 +197,14 @@ class FrequencyChart : ScrollableChart {
rect.centerY() - 0.1f * em, rect.centerY() - 0.1f * em,
pText!! pText!!
) )
if (date[Calendar.MONTH] == 1) canvas.drawText( if (date[Calendar.MONTH] == 1) {
dfYear!!.format(time), canvas.drawText(
rect.centerX(), dfYear!!.format(time),
rect.centerY() + 0.9f * em, rect.centerX(),
pText!! rect.centerY() + 0.9f * em,
) pText!!
)
}
} }
private fun drawGrid(canvas: Canvas, rGrid: RectF?) { private fun drawGrid(canvas: Canvas, rGrid: RectF?) {

@ -148,8 +148,12 @@ class RingView : View {
pRing!!.color = inactiveColor!! pRing!!.color = inactiveColor!!
activeCanvas.drawArc(rect!!, angle - 90, 360 - angle, true, pRing!!) activeCanvas.drawArc(rect!!, angle - 90, 360 - angle, true, pRing!!)
if (thickness > 0) { if (thickness > 0) {
if (isTransparencyEnabled) pRing!!.xfermode = XFERMODE_CLEAR else pRing!!.color = if (isTransparencyEnabled) {
backgroundColor!! pRing!!.xfermode = XFERMODE_CLEAR
} else {
pRing!!.color =
backgroundColor!!
}
rect!!.inset(thickness, thickness) rect!!.inset(thickness, thickness)
activeCanvas.drawArc(rect!!, 0f, 360f, true, pRing!!) activeCanvas.drawArc(rect!!, 0f, 360f, true, pRing!!)
pRing!!.xfermode = null pRing!!.xfermode = null

@ -97,7 +97,7 @@ class ListHabitsRootView @Inject constructor(
title = resources.getString(R.string.main_activity_title), title = resources.getString(R.string.main_activity_title),
color = PaletteColor(17), color = PaletteColor(17),
displayHomeAsUpEnabled = false, displayHomeAsUpEnabled = false,
theme = currentTheme(), theme = currentTheme()
) )
addView(rootView, MATCH_PARENT, MATCH_PARENT) addView(rootView, MATCH_PARENT, MATCH_PARENT)
listAdapter.setListView(listView) listAdapter.setListView(listView)

@ -96,7 +96,7 @@ class ListHabitsScreen
private val colorPickerFactory: ColorPickerDialogFactory, private val colorPickerFactory: ColorPickerDialogFactory,
private val behavior: Lazy<ListHabitsBehavior>, private val behavior: Lazy<ListHabitsBehavior>,
private val preferences: Preferences, private val preferences: Preferences,
private val rootView: Lazy<ListHabitsRootView>, private val rootView: Lazy<ListHabitsRootView>
) : CommandRunner.Listener, ) : CommandRunner.Listener,
ListHabitsBehavior.Screen, ListHabitsBehavior.Screen,
ListHabitsMenuBehavior.Screen, ListHabitsMenuBehavior.Screen,
@ -321,8 +321,11 @@ class ListHabitsScreen
private fun onExportDB() { private fun onExportDB() {
taskRunner.execute( taskRunner.execute(
exportDBFactory.create { filename -> exportDBFactory.create { filename ->
if (filename != null) activity.showSendFileScreen(filename) if (filename != null) {
else activity.showMessage(activity.resources.getString(R.string.could_not_export)) activity.showSendFileScreen(filename)
} else {
activity.showMessage(activity.resources.getString(R.string.could_not_export))
}
} }
) )
} }

@ -60,8 +60,11 @@ abstract class ButtonPanelView<T : View>(
repeat(buttonCount) { buttons.add(createButton()) } repeat(buttonCount) { buttons.add(createButton()) }
removeAllViews() removeAllViews()
if (reverse) buttons.reversed().forEach { addView(it) } if (reverse) {
else buttons.forEach { addView(it) } buttons.reversed().forEach { addView(it) }
} else {
buttons.forEach { addView(it) }
}
setupButtons() setupButtons()
requestLayout() requestLayout()
} }

@ -104,13 +104,19 @@ class CheckmarkButtonView(
} }
override fun onClick(v: View) { override fun onClick(v: View) {
if (preferences.isShortToggleEnabled) performToggle(v) if (preferences.isShortToggleEnabled) {
else onEdit() performToggle(v)
} else {
onEdit()
}
} }
override fun onLongClick(v: View): Boolean { override fun onLongClick(v: View): Boolean {
if (preferences.isShortToggleEnabled) onEdit() if (preferences.isShortToggleEnabled) {
else performToggle(v) onEdit()
} else {
performToggle(v)
}
return true return true
} }
@ -144,8 +150,11 @@ class CheckmarkButtonView(
paint.color = when (value) { paint.color = when (value) {
YES_MANUAL, YES_AUTO, SKIP -> color YES_MANUAL, YES_AUTO, SKIP -> color
NO -> { NO -> {
if (preferences.areQuestionMarksEnabled) mediumContrastColor if (preferences.areQuestionMarksEnabled) {
else lowContrastColor mediumContrastColor
} else {
lowContrastColor
}
} }
else -> lowContrastColor else -> lowContrastColor
} }
@ -153,8 +162,11 @@ class CheckmarkButtonView(
SKIP -> R.string.fa_skipped SKIP -> R.string.fa_skipped
NO -> R.string.fa_times NO -> R.string.fa_times
UNKNOWN -> { UNKNOWN -> {
if (preferences.areQuestionMarksEnabled) R.string.fa_question if (preferences.areQuestionMarksEnabled) {
else R.string.fa_times R.string.fa_question
} else {
R.string.fa_times
}
} }
else -> R.string.fa_check else -> R.string.fa_check
} }

@ -156,10 +156,11 @@ class HabitCardListController @Inject constructor(
} }
private fun notifyListener() { private fun notifyListener() {
if (activeMode is SelectionMode) if (activeMode is SelectionMode) {
selectionMenu.get().onSelectionChange() selectionMenu.get().onSelectionChange()
else } else {
selectionMenu.get().onSelectionFinish() selectionMenu.get().onSelectionFinish()
}
} }
} }
} }

@ -225,7 +225,6 @@ class HabitCardView(
} }
private fun copyAttributesFrom(h: Habit) { private fun copyAttributesFrom(h: Habit) {
fun getActiveColor(habit: Habit): Int { fun getActiveColor(habit: Habit): Int {
return when (habit.isArchived) { return when (habit.isArchived) {
true -> sres.getColor(R.attr.contrast60) true -> sres.getColor(R.attr.contrast60)
@ -271,7 +270,6 @@ class HabitCardView(
} }
private fun updateBackground(isSelected: Boolean) { private fun updateBackground(isSelected: Boolean) {
val background = when (isSelected) { val background = when (isSelected) {
true -> R.drawable.selected_box true -> R.drawable.selected_box
false -> R.drawable.ripple false -> R.drawable.ripple

@ -124,15 +124,20 @@ class HeaderView(
rect.set(0f, 0f, width, height) rect.set(0f, 0f, width, height)
rect.offset(canvas.width.toFloat() - dp(3.0f), 0f) rect.offset(canvas.width.toFloat() - dp(3.0f), 0f)
if (isReversed) rect.offset(-(index + 1) * width, 0f) if (isReversed) {
else rect.offset((index - buttonCount) * width, 0f) rect.offset(-(index + 1) * width, 0f)
} else {
if (isRTL()) rect.set( rect.offset((index - buttonCount) * width, 0f)
canvas.width - rect.right, }
rect.top,
canvas.width - rect.left, if (isRTL()) {
rect.bottom rect.set(
) canvas.width - rect.right,
rect.top,
canvas.width - rect.left,
rect.bottom
)
}
val y1 = rect.centerY() - 0.25 * em val y1 = rect.centerY() - 0.25 * em
val y2 = rect.centerY() + 1.25 * em val y2 = rect.centerY() + 1.25 * em

@ -88,7 +88,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
habit = habit, habit = habit,
habitList = habitList, habitList = habitList,
preferences = preferences, preferences = preferences,
screen = screen, screen = screen
) )
view = ShowHabitView(this) view = ShowHabitView(this)
@ -99,13 +99,13 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
habitList = habitList, habitList = habitList,
screen = screen, screen = screen,
system = HabitsDirFinder(AndroidDirFinder(this)), system = HabitsDirFinder(AndroidDirFinder(this)),
taskRunner = appComponent.taskRunner, taskRunner = appComponent.taskRunner
) )
menu = ShowHabitMenu( menu = ShowHabitMenu(
activity = this, activity = this,
presenter = menuPresenter, presenter = menuPresenter,
preferences = preferences, preferences = preferences
) )
view.setListener(presenter) view.setListener(presenter)
@ -150,7 +150,7 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
ShowHabitPresenter.buildState( ShowHabitPresenter.buildState(
habit = habit, habit = habit,
preferences = preferences, preferences = preferences,
theme = themeSwitcher.currentTheme, theme = themeSwitcher.currentTheme
) )
) )
} }

@ -28,7 +28,7 @@ import org.isoron.uhabits.core.ui.screens.habits.show.ShowHabitMenuPresenter
class ShowHabitMenu( class ShowHabitMenu(
val activity: ShowHabitActivity, val activity: ShowHabitActivity,
val presenter: ShowHabitMenuPresenter, val presenter: ShowHabitMenuPresenter,
val preferences: Preferences, val preferences: Preferences
) { ) {
fun onCreateOptionsMenu(menu: Menu): Boolean { fun onCreateOptionsMenu(menu: Menu): Boolean {
activity.menuInflater.inflate(R.menu.show_habit, menu) activity.menuInflater.inflate(R.menu.show_habit, menu)

@ -39,7 +39,7 @@ class ShowHabitView(context: Context) : FrameLayout(context) {
binding.toolbar, binding.toolbar,
title = data.title, title = data.title,
color = data.color, color = data.color,
theme = data.theme, theme = data.theme
) )
binding.subtitleCard.setState(data.subtitle) binding.subtitleCard.setState(data.subtitle)
binding.overviewCard.setState(data.overview) binding.overviewCard.setState(data.overview)

@ -63,7 +63,7 @@ class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context,
parent: AdapterView<*>?, parent: AdapterView<*>?,
view: View?, view: View?,
position: Int, position: Int,
id: Long, id: Long
) { ) {
presenter.onBoolSpinnerPosition(position) presenter.onBoolSpinnerPosition(position)
} }
@ -77,7 +77,7 @@ class BarCardView(context: Context, attrs: AttributeSet) : LinearLayout(context,
parent: AdapterView<*>?, parent: AdapterView<*>?,
view: View?, view: View?,
position: Int, position: Int,
id: Long, id: Long
) { ) {
presenter.onNumericalSpinnerPosition(position) presenter.onNumericalSpinnerPosition(position)
} }

@ -45,7 +45,7 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont
series = state.series, series = state.series,
defaultSquare = state.defaultSquare, defaultSquare = state.defaultSquare,
notesIndicators = state.notesIndicators, notesIndicators = state.notesIndicators,
firstWeekday = state.firstWeekday, firstWeekday = state.firstWeekday
) )
binding.chart.postInvalidate() binding.chart.postInvalidate()
} }

@ -52,7 +52,7 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
binding.frequencyLabel.text = formatFrequency( binding.frequencyLabel.text = formatFrequency(
state.frequency.numerator, state.frequency.numerator,
state.frequency.denominator, state.frequency.denominator,
resources, resources
) )
binding.questionLabel.setTextColor(color) binding.questionLabel.setTextColor(color)
binding.questionLabel.text = state.question binding.questionLabel.text = state.question

@ -40,7 +40,7 @@ class SettingsActivity : AppCompatActivity() {
toolbar = binding.toolbar, toolbar = binding.toolbar,
title = resources.getString(R.string.settings), title = resources.getString(R.string.settings),
color = PaletteColor(11), color = PaletteColor(11),
theme = themeSwitcher.currentTheme, theme = themeSwitcher.currentTheme
) )
setContentView(binding.root) setContentView(binding.root)
} }

@ -32,7 +32,7 @@ class EditSettingActivity : AppCompatActivity() {
val habits = app.component.habitList.getFiltered( val habits = app.component.habitList.getFiltered(
HabitMatcher( HabitMatcher(
isArchivedAllowed = false, isArchivedAllowed = false,
isCompletedAllowed = true, isCompletedAllowed = true
) )
) )
AndroidThemeSwitcher(this, app.component.preferences).apply() AndroidThemeSwitcher(this, app.component.preferences).apply()
@ -43,7 +43,7 @@ class EditSettingActivity : AppCompatActivity() {
context = this, context = this,
habitList = app.component.habitList, habitList = app.component.habitList,
onSave = controller::onSave, onSave = controller::onSave,
args = args, args = args
) )
setContentView(view) setContentView(view)
} }

@ -54,7 +54,7 @@ class EditSettingRootView(
title = resources.getString(R.string.app_name), title = resources.getString(R.string.app_name),
color = PaletteColor(11), color = PaletteColor(11),
displayHomeAsUpEnabled = false, displayHomeAsUpEnabled = false,
theme = currentTheme(), theme = currentTheme()
) )
populateHabitSpinner() populateHabitSpinner()
binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { binding.habitSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
@ -69,7 +69,7 @@ class EditSettingRootView(
val habit = habitList.getByPosition(binding.habitSpinner.selectedItemPosition) val habit = habitList.getByPosition(binding.habitSpinner.selectedItemPosition)
val action = mapSpinnerPositionToAction( val action = mapSpinnerPositionToAction(
isNumerical = habit.isNumerical, isNumerical = habit.isNumerical,
itemPosition = binding.actionSpinner.selectedItemPosition, itemPosition = binding.actionSpinner.selectedItemPosition
) )
onSave(habit, action) onSave(habit, action)
} }

@ -27,22 +27,34 @@ class AndroidCursor(private val cursor: android.database.Cursor) : Cursor {
override fun moveToNext() = cursor.moveToNext() override fun moveToNext() = cursor.moveToNext()
override fun getInt(index: Int): Int? { override fun getInt(index: Int): Int? {
return if (cursor.isNull(index)) null return if (cursor.isNull(index)) {
else cursor.getInt(index) null
} else {
cursor.getInt(index)
}
} }
override fun getLong(index: Int): Long? { override fun getLong(index: Int): Long? {
return if (cursor.isNull(index)) null return if (cursor.isNull(index)) {
else cursor.getLong(index) null
} else {
cursor.getLong(index)
}
} }
override fun getDouble(index: Int): Double? { override fun getDouble(index: Int): Double? {
return if (cursor.isNull(index)) null return if (cursor.isNull(index)) {
else cursor.getDouble(index) null
} else {
cursor.getDouble(index)
}
} }
override fun getString(index: Int): String? { override fun getString(index: Int): String? {
return if (cursor.isNull(index)) null return if (cursor.isNull(index)) {
else cursor.getString(index) null
} else {
cursor.getString(index)
}
} }
} }

@ -26,7 +26,7 @@ import java.io.File
class AndroidDatabase( class AndroidDatabase(
private val db: SQLiteDatabase, private val db: SQLiteDatabase,
override val file: File?, override val file: File?
) : Database { ) : Database {
override fun beginTransaction() = db.beginTransaction() override fun beginTransaction() = db.beginTransaction()
@ -45,7 +45,7 @@ class AndroidDatabase(
tableName: String, tableName: String,
values: Map<String, Any?>, values: Map<String, Any?>,
where: String, where: String,
vararg params: String, vararg params: String
): Int { ): Int {
val contValues = mapToContentValues(values) val contValues = mapToContentValues(values)
return db.update(tableName, contValues, where, params) return db.update(tableName, contValues, where, params)
@ -59,7 +59,7 @@ class AndroidDatabase(
override fun delete( override fun delete(
tableName: String, tableName: String,
where: String, where: String,
vararg params: String, vararg params: String
) { ) {
db.delete(tableName, where, params) db.delete(tableName, where, params)
} }

@ -30,7 +30,7 @@ class AndroidDatabaseOpener @Inject constructor() : DatabaseOpener {
db = SQLiteDatabase.openDatabase( db = SQLiteDatabase.openDatabase(
file.absolutePath, file.absolutePath,
null, null,
SQLiteDatabase.OPEN_READWRITE, SQLiteDatabase.OPEN_READWRITE
), ),
file = file file = file
) )

@ -25,5 +25,6 @@ import dagger.Provides
@Module @Module
class ActivityContextModule( class ActivityContextModule(
@get:Provides @get:Provides
@get:ActivityContext val context: Context @get:ActivityContext
val context: Context
) )

@ -26,5 +26,6 @@ import dagger.Provides
class AppContextModule( class AppContextModule(
@get:Provides @get:Provides
@get:AppContext @get:AppContext
@param:AppContext val context: Context @param:AppContext
val context: Context
) )

@ -53,8 +53,9 @@ class IntentParser
var timestamp = intent.getLongExtra("timestamp", today) var timestamp = intent.getLongExtra("timestamp", today)
timestamp = DateUtils.getStartOfDay(timestamp) timestamp = DateUtils.getStartOfDay(timestamp)
if (timestamp < 0 || timestamp > today) if (timestamp < 0 || timestamp > today) {
throw IllegalArgumentException("timestamp is not valid") throw IllegalArgumentException("timestamp is not valid")
}
return Timestamp(timestamp) return Timestamp(timestamp)
} }

@ -96,7 +96,6 @@ class AndroidNotificationTray
timestamp: Timestamp, timestamp: Timestamp,
disableSound: Boolean = false disableSound: Boolean = false
): Notification { ): Notification {
val addRepetitionAction = Action( val addRepetitionAction = Action(
R.drawable.ic_action_check, R.drawable.ic_action_check,
context.getString(R.string.yes), context.getString(R.string.yes),
@ -146,8 +145,9 @@ class AndroidNotificationTray
.addAction(removeRepetitionAction) .addAction(removeRepetitionAction)
} }
if (!disableSound) if (!disableSound) {
builder.setSound(ringtoneManager.getURI()) builder.setSound(ringtoneManager.getURI())
}
if (SDK_INT < Build.VERSION_CODES.S) { if (SDK_INT < Build.VERSION_CODES.S) {
val snoozeAction = Action( val snoozeAction = Action(

@ -61,8 +61,9 @@ class RingtoneManager
"pref_ringtone_uri", "pref_ringtone_uri",
defaultRingtoneUri.toString() defaultRingtoneUri.toString()
)!! )!!
if (prefRingtoneUri.isNotEmpty()) if (prefRingtoneUri.isNotEmpty()) {
ringtoneUri = Uri.parse(prefRingtoneUri) ringtoneUri = Uri.parse(prefRingtoneUri)
}
return ringtoneUri return ringtoneUri
} }

@ -103,7 +103,9 @@ class SnoozeDelayPickerActivity : FragmentActivity(), OnItemClickListener {
if (snoozeValues[position] >= 0) { if (snoozeValues[position] >= 0) {
reminderController!!.onSnoozeDelayPicked(habit!!, snoozeValues[position]) reminderController!!.onSnoozeDelayPicked(habit!!, snoozeValues[position])
finish() finish()
} else showTimePicker() } else {
showTimePicker()
}
} }
override fun finish() { override fun finish() {

@ -27,7 +27,7 @@ import javax.inject.Inject
class ExportDBTaskFactory class ExportDBTaskFactory
@Inject constructor( @Inject constructor(
@AppContext private val context: Context, @AppContext private val context: Context,
private val system: AndroidDirFinder, private val system: AndroidDirFinder
) { ) {
fun create(listener: ExportDBTask.Listener) = ExportDBTask(context, system, listener) fun create(listener: ExportDBTask.Listener) = ExportDBTask(context, system, listener)
} }

@ -27,7 +27,7 @@ import javax.inject.Inject
class ImportDataTaskFactory class ImportDataTaskFactory
@Inject constructor( @Inject constructor(
private val importer: GenericImporter, private val importer: GenericImporter,
private val modelFactory: ModelFactory, private val modelFactory: ModelFactory
) { ) {
fun create(file: File, listener: ImportDataTask.Listener) = fun create(file: File, listener: ImportDataTask.Listener) =
ImportDataTask(importer, modelFactory, file, listener) ImportDataTask(importer, modelFactory, file, listener)

@ -24,6 +24,7 @@ import org.jetbrains.annotations.Contract
object AttributeSetUtils { object AttributeSetUtils {
const val ISORON_NAMESPACE = "http://isoron.org/android" const val ISORON_NAMESPACE = "http://isoron.org/android"
@JvmStatic @JvmStatic
fun getAttribute( fun getAttribute(
context: Context, context: Context,

@ -33,6 +33,7 @@ import java.text.SimpleDateFormat
object DatabaseUtils { object DatabaseUtils {
private var opener: HabitsDatabaseOpener? = null private var opener: HabitsDatabaseOpener? = null
@JvmStatic @JvmStatic
fun getDatabaseFile(context: Context): File { fun getDatabaseFile(context: Context): File {
val databaseFilename = databaseFilename val databaseFilename = databaseFilename

@ -67,7 +67,6 @@ fun RelativeLayout.addBelow(
height: Int = WRAP_CONTENT, height: Int = WRAP_CONTENT,
applyCustomRules: (params: RelativeLayout.LayoutParams) -> Unit = {} applyCustomRules: (params: RelativeLayout.LayoutParams) -> Unit = {}
) { ) {
view.layoutParams = RelativeLayout.LayoutParams(width, height).apply { view.layoutParams = RelativeLayout.LayoutParams(width, height).apply {
addRule(BELOW, subject.id) addRule(BELOW, subject.id)
applyCustomRules(this) applyCustomRules(this)
@ -81,7 +80,6 @@ fun RelativeLayout.addAtBottom(
width: Int = MATCH_PARENT, width: Int = MATCH_PARENT,
height: Int = WRAP_CONTENT height: Int = WRAP_CONTENT
) { ) {
view.layoutParams = RelativeLayout.LayoutParams(width, height).apply { view.layoutParams = RelativeLayout.LayoutParams(width, height).apply {
addRule(ALIGN_PARENT_BOTTOM) addRule(ALIGN_PARENT_BOTTOM)
} }
@ -96,7 +94,6 @@ fun RelativeLayout.addAtTop(
width: Int = MATCH_PARENT, width: Int = MATCH_PARENT,
height: Int = WRAP_CONTENT height: Int = WRAP_CONTENT
) { ) {
view.layoutParams = RelativeLayout.LayoutParams(width, height).apply { view.layoutParams = RelativeLayout.LayoutParams(width, height).apply {
addRule(ALIGN_PARENT_TOP) addRule(ALIGN_PARENT_TOP)
} }
@ -196,7 +193,7 @@ fun Activity.restartWithFade(cls: Class<*>?) {
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
startActivity(Intent(this, cls)) startActivity(Intent(this, cls))
}, },
500, 500
) // HACK: Let the menu disappear first ) // HACK: Let the menu disappear first
} }
@ -205,7 +202,7 @@ fun View.setupToolbar(
title: String, title: String,
color: PaletteColor, color: PaletteColor,
theme: Theme, theme: Theme,
displayHomeAsUpEnabled: Boolean = true, displayHomeAsUpEnabled: Boolean = true
) { ) {
toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f) toolbar.elevation = InterfaceUtils.dpToPixels(context, 2f)
val res = StyledResources(context) val res = StyledResources(context)

@ -147,10 +147,11 @@ abstract class BaseWidget(val context: Context, val id: Int, val stacked: Boolea
protected val preferedBackgroundAlpha: Int protected val preferedBackgroundAlpha: Int
get() { get() {
return if (stacked) return if (stacked) {
255 255
else } else {
prefs.widgetOpacity prefs.widgetOpacity
}
} }
init { init {

@ -33,7 +33,7 @@ open class CheckmarkWidget(
context: Context, context: Context,
widgetId: Int, widgetId: Int,
protected val habit: Habit, protected val habit: Habit,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, widgetId, stacked) { ) : BaseWidget(context, widgetId, stacked) {
override val defaultHeight: Int = 125 override val defaultHeight: Int = 125

@ -23,7 +23,10 @@ import android.content.Context
class CheckmarkWidgetProvider : BaseWidgetProvider() { class CheckmarkWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
return if (habits.size == 1) CheckmarkWidget(context, id, habits[0]) return if (habits.size == 1) {
else StackWidget(context, id, StackWidgetType.CHECKMARK, habits) CheckmarkWidget(context, id, habits[0])
} else {
StackWidget(context, id, StackWidgetType.CHECKMARK, habits)
}
} }
} }

@ -27,7 +27,7 @@ import org.isoron.uhabits.widgets.views.EmptyWidgetView
class EmptyWidget( class EmptyWidget(
context: Context, context: Context,
widgetId: Int, widgetId: Int,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, widgetId, stacked) { ) : BaseWidget(context, widgetId, stacked) {
override val defaultHeight: Int = 200 override val defaultHeight: Int = 200
override val defaultWidth: Int = 200 override val defaultWidth: Int = 200

@ -33,7 +33,7 @@ class FrequencyWidget(
widgetId: Int, widgetId: Int,
private val habit: Habit, private val habit: Habit,
private val firstWeekday: Int, private val firstWeekday: Int,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, widgetId, stacked) { ) : BaseWidget(context, widgetId, stacked) {
override val defaultHeight: Int = 200 override val defaultHeight: Int = 200
override val defaultWidth: Int = 200 override val defaultWidth: Int = 200

@ -24,12 +24,15 @@ import android.content.Context
class FrequencyWidgetProvider : BaseWidgetProvider() { class FrequencyWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
return if (habits.size == 1) FrequencyWidget( return if (habits.size == 1) {
context, FrequencyWidget(
id, context,
habits[0], id,
preferences.firstWeekdayInt habits[0],
) preferences.firstWeekdayInt
else StackWidget(context, id, StackWidgetType.FREQUENCY, habits) )
} else {
StackWidget(context, id, StackWidgetType.FREQUENCY, habits)
}
} }
} }

@ -36,7 +36,7 @@ class HistoryWidget(
context: Context, context: Context,
id: Int, id: Int,
private val habit: Habit, private val habit: Habit,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, id, stacked) { ) : BaseWidget(context, id, stacked) {
override val defaultHeight: Int = 250 override val defaultHeight: Int = 250
@ -53,7 +53,7 @@ class HistoryWidget(
val model = HistoryCardPresenter.buildState( val model = HistoryCardPresenter.buildState(
habit = habit, habit = habit,
firstWeekday = prefs.firstWeekday, firstWeekday = prefs.firstWeekday,
theme = WidgetTheme(), theme = WidgetTheme()
) )
(widgetView.dataView as AndroidDataView).apply { (widgetView.dataView as AndroidDataView).apply {
val historyChart = (this.view as HistoryChart) val historyChart = (this.view as HistoryChart)
@ -75,7 +75,7 @@ class HistoryWidget(
firstWeekday = prefs.firstWeekday, firstWeekday = prefs.firstWeekday,
series = listOf(), series = listOf(),
defaultSquare = HistoryChart.Square.OFF, defaultSquare = HistoryChart.Square.OFF,
notesIndicators = listOf(), notesIndicators = listOf()
) )
} }
).apply { ).apply {

@ -23,11 +23,14 @@ import android.content.Context
class HistoryWidgetProvider : BaseWidgetProvider() { class HistoryWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
return if (habits.size == 1) HistoryWidget( return if (habits.size == 1) {
context, HistoryWidget(
id, context,
habits[0] id,
) habits[0]
else StackWidget(context, id, StackWidgetType.HISTORY, habits) )
} else {
StackWidget(context, id, StackWidgetType.HISTORY, habits)
}
} }
} }

@ -33,7 +33,7 @@ class ScoreWidget(
context: Context, context: Context,
id: Int, id: Int,
private val habit: Habit, private val habit: Habit,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, id, stacked) { ) : BaseWidget(context, id, stacked) {
override val defaultHeight: Int = 300 override val defaultHeight: Int = 300
override val defaultWidth: Int = 300 override val defaultWidth: Int = 300
@ -46,7 +46,7 @@ class ScoreWidget(
habit = habit, habit = habit,
firstWeekday = prefs.firstWeekdayInt, firstWeekday = prefs.firstWeekdayInt,
spinnerPosition = prefs.scoreCardSpinnerPosition, spinnerPosition = prefs.scoreCardSpinnerPosition,
theme = WidgetTheme(), theme = WidgetTheme()
) )
val widgetView = view as GraphWidgetView val widgetView = view as GraphWidgetView
widgetView.setBackgroundAlpha(preferedBackgroundAlpha) widgetView.setBackgroundAlpha(preferedBackgroundAlpha)

@ -23,7 +23,10 @@ import android.content.Context
class ScoreWidgetProvider : BaseWidgetProvider() { class ScoreWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
return if (habits.size == 1) ScoreWidget(context, id, habits[0]) return if (habits.size == 1) {
else StackWidget(context, id, StackWidgetType.SCORE, habits) ScoreWidget(context, id, habits[0])
} else {
StackWidget(context, id, StackWidgetType.SCORE, habits)
}
} }
} }

@ -34,7 +34,7 @@ class StackWidget(
widgetId: Int, widgetId: Int,
private val widgetType: StackWidgetType, private val widgetType: StackWidgetType,
private val habits: List<Habit>, private val habits: List<Habit>,
stacked: Boolean = true, stacked: Boolean = true
) : BaseWidget(context, widgetId, stacked) { ) : BaseWidget(context, widgetId, stacked) {
override val defaultHeight: Int = 0 override val defaultHeight: Int = 0
override val defaultWidth: Int = 0 override val defaultWidth: Int = 0

@ -34,7 +34,7 @@ class StreakWidget(
context: Context, context: Context,
id: Int, id: Int,
private val habit: Habit, private val habit: Habit,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, id, stacked) { ) : BaseWidget(context, id, stacked) {
override val defaultHeight: Int = 200 override val defaultHeight: Int = 200
override val defaultWidth: Int = 200 override val defaultWidth: Int = 200

@ -23,7 +23,10 @@ import android.content.Context
class StreakWidgetProvider : BaseWidgetProvider() { class StreakWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
return if (habits.size == 1) StreakWidget(context, id, habits[0]) return if (habits.size == 1) {
else StackWidget(context, id, StackWidgetType.STREAKS, habits) StreakWidget(context, id, habits[0])
} else {
StackWidget(context, id, StackWidgetType.STREAKS, habits)
}
} }
} }

@ -37,7 +37,7 @@ class TargetWidget(
context: Context, context: Context,
id: Int, id: Int,
private val habit: Habit, private val habit: Habit,
stacked: Boolean = false, stacked: Boolean = false
) : BaseWidget(context, id, stacked) { ) : BaseWidget(context, id, stacked) {
override val defaultHeight: Int = 200 override val defaultHeight: Int = 200
override val defaultWidth: Int = 200 override val defaultWidth: Int = 200
@ -53,7 +53,7 @@ class TargetWidget(
val data = TargetCardPresenter.buildState( val data = TargetCardPresenter.buildState(
habit = habit, habit = habit,
firstWeekday = prefs.firstWeekdayInt, firstWeekday = prefs.firstWeekdayInt,
theme = WidgetTheme(), theme = WidgetTheme()
) )
chart.setColor(WidgetTheme().color(habit.color).toInt()) chart.setColor(WidgetTheme().color(habit.color).toInt())
chart.setTargets(data.targets) chart.setTargets(data.targets)

@ -23,7 +23,10 @@ import android.content.Context
class TargetWidgetProvider : BaseWidgetProvider() { class TargetWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val habits = getHabitsFromWidgetId(id) val habits = getHabitsFromWidgetId(id)
return if (habits.size == 1) TargetWidget(context, id, habits[0]) return if (habits.size == 1) {
else StackWidget(context, id, StackWidgetType.TARGET, habits) TargetWidget(context, id, habits[0])
} else {
StackWidget(context, id, StackWidgetType.TARGET, habits)
}
} }
} }

@ -96,21 +96,23 @@ class CheckmarkWidgetView : HabitWidgetView {
private val text: String private val text: String
get() = if (isNumerical) { get() = if (isNumerical) {
(max(0, entryValue) / 1000.0).toShortString() (max(0, entryValue) / 1000.0).toShortString()
} else when (entryState) { } else {
YES_MANUAL, YES_AUTO -> resources.getString(R.string.fa_check) when (entryState) {
SKIP -> resources.getString(R.string.fa_skipped) YES_MANUAL, YES_AUTO -> resources.getString(R.string.fa_check)
UNKNOWN -> { SKIP -> resources.getString(R.string.fa_skipped)
run { UNKNOWN -> {
if (preferences!!.areQuestionMarksEnabled) { run {
return resources.getString(R.string.fa_question) if (preferences!!.areQuestionMarksEnabled) {
} else { return resources.getString(R.string.fa_question)
resources.getString(R.string.fa_times) } else {
resources.getString(R.string.fa_times)
}
} }
resources.getString(R.string.fa_times)
} }
resources.getString(R.string.fa_times) NO -> resources.getString(R.string.fa_times)
else -> resources.getString(R.string.fa_times)
} }
NO -> resources.getString(R.string.fa_times)
else -> resources.getString(R.string.fa_times)
} }
override val innerLayoutId: Int override val innerLayoutId: Int

@ -18,7 +18,6 @@
*/ */
package org.isoron.uhabits package org.isoron.uhabits
import com.nhaarman.mockitokotlin2.spy
import org.isoron.uhabits.core.commands.CommandRunner import org.isoron.uhabits.core.commands.CommandRunner
import org.isoron.uhabits.core.models.HabitList import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.memory.MemoryModelFactory import org.isoron.uhabits.core.models.memory.MemoryModelFactory
@ -31,6 +30,7 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.spy
@RunWith(MockitoJUnitRunner::class) @RunWith(MockitoJUnitRunner::class)
open class BaseAndroidJVMTest { open class BaseAndroidJVMTest {

@ -18,9 +18,6 @@
*/ */
package org.isoron.uhabits.receivers package org.isoron.uhabits.receivers
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
import org.isoron.uhabits.BaseAndroidJVMTest import org.isoron.uhabits.BaseAndroidJVMTest
import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.Timestamp import org.isoron.uhabits.core.models.Timestamp
@ -28,6 +25,9 @@ import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.reminders.ReminderScheduler import org.isoron.uhabits.core.reminders.ReminderScheduler
import org.isoron.uhabits.core.ui.NotificationTray import org.isoron.uhabits.core.ui.NotificationTray
import org.junit.Test import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
class ReminderControllerTest : BaseAndroidJVMTest() { class ReminderControllerTest : BaseAndroidJVMTest() {
private lateinit var controller: ReminderController private lateinit var controller: ReminderController

@ -61,7 +61,7 @@ kotlin {
implementation("org.xerial:sqlite-jdbc:3.40.0.0") implementation("org.xerial:sqlite-jdbc:3.40.0.0")
implementation("org.hamcrest:hamcrest:2.2") implementation("org.hamcrest:hamcrest:2.2")
implementation("org.apache.commons:commons-io:1.3.2") implementation("org.apache.commons:commons-io:1.3.2")
implementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0") implementation("org.mockito.kotlin:mockito-kotlin:2.2.11")
} }
} }
} }

@ -31,7 +31,7 @@ enum class Font {
data class ScreenLocation( data class ScreenLocation(
val x: Double, val x: Double,
val y: Double, val y: Double
) )
interface Canvas { interface Canvas {

@ -23,7 +23,7 @@ data class Color(
val red: Double, val red: Double,
val green: Double, val green: Double,
val blue: Double, val blue: Double,
val alpha: Double, val alpha: Double
) { ) {
val luminosity: Double val luminosity: Double
get() { get() {

@ -29,7 +29,7 @@ enum class DayOfWeek(val daysSinceSunday: Int) {
WEDNESDAY(3), WEDNESDAY(3),
THURSDAY(4), THURSDAY(4),
FRIDAY(5), FRIDAY(5),
SATURDAY(6), SATURDAY(6)
} }
data class LocalDate(val daysSince2000: Int) { data class LocalDate(val daysSince2000: Int) {
@ -155,7 +155,6 @@ val nonLeapOffset = arrayOf(
) )
private fun daysSince2000(year: Int, month: Int, day: Int): Int { private fun daysSince2000(year: Int, month: Int, day: Int): Int {
var result = 365 * (year - 2000) var result = 365 * (year - 2000)
result += ceil((year - 2000) / 4.0).toInt() result += ceil((year - 2000) / 4.0).toInt()
result -= ceil((year - 2000) / 100.0).toInt() result -= ceil((year - 2000) / 100.0).toInt()

@ -37,7 +37,7 @@ import kotlin.math.roundToInt
class JavaCanvas( class JavaCanvas(
val image: BufferedImage, val image: BufferedImage,
val pixelScale: Double = 2.0, val pixelScale: Double = 2.0
) : Canvas { ) : Canvas {
override fun toImage(): Image { override fun toImage(): Image {
@ -131,7 +131,7 @@ class JavaCanvas(
y: Double, y: Double,
width: Double, width: Double,
height: Double, height: Double,
cornerRadius: Double, cornerRadius: Double
) { ) {
g2d.fill( g2d.fill(
RoundRectangle2D.Double( RoundRectangle2D.Double(
@ -140,7 +140,7 @@ class JavaCanvas(
toPixel(width).toDouble(), toPixel(width).toDouble(),
toPixel(height).toDouble(), toPixel(height).toDouble(),
toPixel(cornerRadius).toDouble(), toPixel(cornerRadius).toDouble(),
toPixel(cornerRadius).toDouble(), toPixel(cornerRadius).toDouble()
) )
) )
} }
@ -194,9 +194,8 @@ class JavaCanvas(
centerY: Double, centerY: Double,
radius: Double, radius: Double,
startAngle: Double, startAngle: Double,
swipeAngle: Double, swipeAngle: Double
) { ) {
g2d.fillArc( g2d.fillArc(
toPixel(centerX - radius), toPixel(centerX - radius),
toPixel(centerY - radius), toPixel(centerY - radius),

@ -33,8 +33,11 @@ class JavaResourceFile(val path: String) : ResourceFile {
get() { get() {
val mainPath = Paths.get("assets/main/$path") val mainPath = Paths.get("assets/main/$path")
val testPath = Paths.get("assets/test/$path") val testPath = Paths.get("assets/test/$path")
return if (Files.exists(mainPath)) mainPath return if (Files.exists(mainPath)) {
else testPath mainPath
} else {
testPath
}
} }
override suspend fun exists(): Boolean { override suspend fun exists(): Boolean {

@ -23,7 +23,7 @@ import org.isoron.uhabits.core.models.HabitList
data class ArchiveHabitsCommand( data class ArchiveHabitsCommand(
val habitList: HabitList, val habitList: HabitList,
val selected: List<Habit>, val selected: List<Habit>
) : Command { ) : Command {
override fun run() { override fun run() {
for (h in selected) h.isArchived = true for (h in selected) h.isArchived = true

@ -25,7 +25,7 @@ import org.isoron.uhabits.core.models.PaletteColor
data class ChangeHabitColorCommand( data class ChangeHabitColorCommand(
val habitList: HabitList, val habitList: HabitList,
val selected: List<Habit>, val selected: List<Habit>,
val newColor: PaletteColor, val newColor: PaletteColor
) : Command { ) : Command {
override fun run() { override fun run() {
for (h in selected) h.color = newColor for (h in selected) h.color = newColor

@ -27,7 +27,7 @@ import javax.inject.Inject
@AppScope @AppScope
open class CommandRunner open class CommandRunner
@Inject constructor( @Inject constructor(
private val taskRunner: TaskRunner, private val taskRunner: TaskRunner
) { ) {
private val listeners: LinkedList<Listener> = LinkedList() private val listeners: LinkedList<Listener> = LinkedList()

@ -25,7 +25,7 @@ import org.isoron.uhabits.core.models.ModelFactory
data class CreateHabitCommand( data class CreateHabitCommand(
val modelFactory: ModelFactory, val modelFactory: ModelFactory,
val habitList: HabitList, val habitList: HabitList,
val model: Habit, val model: Habit
) : Command { ) : Command {
override fun run() { override fun run() {
val habit = modelFactory.buildHabit() val habit = modelFactory.buildHabit()

@ -28,7 +28,7 @@ data class CreateRepetitionCommand(
val habit: Habit, val habit: Habit,
val timestamp: Timestamp, val timestamp: Timestamp,
val value: Int, val value: Int,
val notes: String, val notes: String
) : Command { ) : Command {
override fun run() { override fun run() {
val entries = habit.originalEntries val entries = habit.originalEntries

@ -23,7 +23,7 @@ import org.isoron.uhabits.core.models.HabitList
data class DeleteHabitsCommand( data class DeleteHabitsCommand(
val habitList: HabitList, val habitList: HabitList,
val selected: List<Habit>, val selected: List<Habit>
) : Command { ) : Command {
override fun run() { override fun run() {
for (h in selected) habitList.remove(h) for (h in selected) habitList.remove(h)

@ -25,7 +25,7 @@ import org.isoron.uhabits.core.models.HabitNotFoundException
data class EditHabitCommand( data class EditHabitCommand(
val habitList: HabitList, val habitList: HabitList,
val habitId: Long, val habitId: Long,
val modified: Habit, val modified: Habit
) : Command { ) : Command {
override fun run() { override fun run() {
val habit = habitList.getById(habitId) ?: throw HabitNotFoundException() val habit = habitList.getById(habitId) ?: throw HabitNotFoundException()

@ -23,7 +23,7 @@ import org.isoron.uhabits.core.models.HabitList
data class UnarchiveHabitsCommand( data class UnarchiveHabitsCommand(
val habitList: HabitList, val habitList: HabitList,
val selected: List<Habit>, val selected: List<Habit>
) : Command { ) : Command {
override fun run() { override fun run() {
for (h in selected) h.isArchived = false for (h in selected) h.isArchived = false

@ -35,7 +35,7 @@ interface Database {
tableName: String, tableName: String,
values: Map<String, Any?>, values: Map<String, Any?>,
where: String, where: String,
vararg params: String, vararg params: String
): Int ): Int
fun insert(tableName: String, values: Map<String, Any?>): Long? fun insert(tableName: String, values: Map<String, Any?>): Long?

@ -41,7 +41,7 @@ class JdbcDatabase(private val connection: Connection) : Database {
tableName: String, tableName: String,
values: Map<String, Any?>, values: Map<String, Any?>,
where: String, where: String,
vararg params: String, vararg params: String
): Int { ): Int {
return try { return try {
val fields = ArrayList<String?>() val fields = ArrayList<String?>()

@ -24,7 +24,7 @@ import java.io.InputStream
import java.util.Locale import java.util.Locale
class MigrationHelper( class MigrationHelper(
private val db: Database, private val db: Database
) { ) {
fun migrateTo(newVersion: Int) { fun migrateTo(newVersion: Int) {
try { try {

@ -28,7 +28,7 @@ import java.util.LinkedList
class Repository<T>( class Repository<T>(
private val klass: Class<T>, private val klass: Class<T>,
private val db: Database, private val db: Database
) { ) {
/** /**
* Returns the record that has the id provided. If no record is found, returns null. * Returns the record that has the id provided. If no record is found, returns null.

@ -20,7 +20,7 @@ import java.io.InputStream
import java.util.ArrayList import java.util.ArrayList
internal class Tokenizer( internal class Tokenizer(
private val mStream: InputStream, private val mStream: InputStream
) { ) {
private var mIsNext = false private var mIsNext = false
private var mCurrent = 0 private var mCurrent = 0

@ -30,14 +30,14 @@ class GenericImporter
loopDBImporter: LoopDBImporter, loopDBImporter: LoopDBImporter,
rewireDBImporter: RewireDBImporter, rewireDBImporter: RewireDBImporter,
tickmateDBImporter: TickmateDBImporter, tickmateDBImporter: TickmateDBImporter,
habitBullCSVImporter: HabitBullCSVImporter, habitBullCSVImporter: HabitBullCSVImporter
) : AbstractImporter() { ) : AbstractImporter() {
var importers: List<AbstractImporter> = listOf( var importers: List<AbstractImporter> = listOf(
loopDBImporter, loopDBImporter,
rewireDBImporter, rewireDBImporter,
tickmateDBImporter, tickmateDBImporter,
habitBullCSVImporter, habitBullCSVImporter
) )
override fun canHandle(file: File): Boolean { override fun canHandle(file: File): Boolean {

@ -48,7 +48,7 @@ class HabitBullCSVImporter
@Inject constructor( @Inject constructor(
private val habitList: HabitList, private val habitList: HabitList,
private val modelFactory: ModelFactory, private val modelFactory: ModelFactory,
logging: Logging, logging: Logging
) : AbstractImporter() { ) : AbstractImporter() {
private val logger = logging.getLogger("HabitBullCSVImporter") private val logger = logging.getLogger("HabitBullCSVImporter")
@ -98,7 +98,7 @@ class HabitBullCSVImporter
val formats = listOf( val formats = listOf(
DateFormat.getDateInstance(DateFormat.SHORT), DateFormat.getDateInstance(DateFormat.SHORT),
SimpleDateFormat("yyyy-MM-dd", Locale.US), SimpleDateFormat("yyyy-MM-dd", Locale.US),
SimpleDateFormat("MM/dd/yyyy", Locale.US), SimpleDateFormat("MM/dd/yyyy", Locale.US)
) )
var parsedDate: Date? = null var parsedDate: Date? = null
for (fmt in formats) { for (fmt in formats) {
@ -116,7 +116,7 @@ class HabitBullCSVImporter
return Timestamp.from( return Timestamp.from(
parsedCalendar[YEAR], parsedCalendar[YEAR],
parsedCalendar[MONTH], parsedCalendar[MONTH],
parsedCalendar[DAY_OF_MONTH], parsedCalendar[DAY_OF_MONTH]
) )
} }

@ -45,7 +45,7 @@ class LoopDBImporter
@AppScope val modelFactory: ModelFactory, @AppScope val modelFactory: ModelFactory,
@AppScope val opener: DatabaseOpener, @AppScope val opener: DatabaseOpener,
@AppScope val runner: CommandRunner, @AppScope val runner: CommandRunner,
@AppScope logging: Logging, @AppScope logging: Logging
) : AbstractImporter() { ) : AbstractImporter() {
private val logger = logging.getLogger("LoopDBImporter") private val logger = logging.getLogger("LoopDBImporter")

@ -125,7 +125,7 @@ class RewireDBImporter
try { try {
c = db.query( c = db.query(
"select distinct date from checkins where habit_id=? and type=2", "select distinct date from checkins where habit_id=? and type=2",
rewireHabitId.toString(), rewireHabitId.toString()
) )
if (!c.moveToNext()) return if (!c.moveToNext()) return
do { do {
@ -147,7 +147,7 @@ class RewireDBImporter
try { try {
c = db.query( c = db.query(
"select time, active_days from reminders where habit_id=? limit 1", "select time, active_days from reminders where habit_id=? limit 1",
rewireHabitId.toString(), rewireHabitId.toString()
) )
if (!c.moveToNext()) return if (!c.moveToNext()) return
val rewireReminder = c.getInt(0)!! val rewireReminder = c.getInt(0)!!

@ -72,7 +72,7 @@ class TickmateDBImporter @Inject constructor(
try { try {
c = db.query( c = db.query(
"select distinct year, month, day from ticks where _track_id=?", "select distinct year, month, day from ticks where _track_id=?",
tickmateTrackId.toString(), tickmateTrackId.toString()
) )
if (!c.moveToNext()) return if (!c.moveToNext()) return
do { do {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save