@@ -1,11 +1,11 @@
|
|||||||
plugins {
|
plugins {
|
||||||
val kotlinVersion = "1.5.0"
|
val kotlinVersion = "1.5.0"
|
||||||
id("com.android.application") version ("7.0.2") apply (false)
|
id("com.android.application") version ("7.0.3") 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 "10.1.0"
|
id("org.jlleitschuh.gradle.ktlint") version "10.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply {
|
apply {
|
||||||
@@ -18,7 +18,6 @@ allprojects {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven(url = "https://plugins.gradle.org/m2/")
|
maven(url = "https://plugins.gradle.org/m2/")
|
||||||
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
|
maven(url = "https://oss.sonatype.org/content/repositories/snapshots/")
|
||||||
maven(url = "https://kotlin.bintray.com/ktor")
|
maven(url = "https://jitpack.io")
|
||||||
maven(url = "https://kotlin.bintray.com/kotlin-js-wrappers")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
org.gradle.parallel=false
|
org.gradle.parallel=false
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
org.gradle.jvmargs=-Xms2048m -Xmx2048m
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.github.triplet.play") version "3.6.0"
|
id("com.github.triplet.play") version "3.7.0"
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
id("org.jetbrains.kotlin.kapt")
|
id("org.jetbrains.kotlin.kapt")
|
||||||
@@ -32,13 +32,13 @@ tasks.compileLint {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
compileSdk = 30
|
compileSdk = 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
versionCode = 20003
|
versionCode = 20003
|
||||||
versionName = "2.0.3"
|
versionName = "2.0.3"
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = 30
|
targetSdk = 31
|
||||||
applicationId = "org.isoron.uhabits"
|
applicationId = "org.isoron.uhabits"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -86,10 +86,10 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
val daggerVersion = "2.38.1"
|
val daggerVersion = "2.41"
|
||||||
val kotlinVersion = "1.5.30"
|
val kotlinVersion = "1.6.10"
|
||||||
val kxCoroutinesVersion = "1.5.1"
|
val kxCoroutinesVersion = "1.6.0"
|
||||||
val ktorVersion = "1.6.3"
|
val ktorVersion = "1.6.7"
|
||||||
val espressoVersion = "3.4.0"
|
val espressoVersion = "3.4.0"
|
||||||
|
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion")
|
androidTestImplementation("androidx.test.espresso:espresso-contrib:$espressoVersion")
|
||||||
@@ -98,17 +98,17 @@ dependencies {
|
|||||||
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.1")
|
androidTestImplementation("com.linkedin.dexmaker:dexmaker-mockito:2.28.1")
|
||||||
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
|
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
|
||||||
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
|
androidTestImplementation("io.ktor:ktor-jackson:$ktorVersion")
|
||||||
androidTestImplementation("androidx.annotation:annotation:1.2.0")
|
androidTestImplementation("androidx.annotation:annotation:1.3.0")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||||
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.2.0")
|
||||||
androidTestImplementation("androidx.test:rules:1.4.0")
|
androidTestImplementation("androidx.test:rules:1.4.0")
|
||||||
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
androidTestImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0")
|
||||||
compileOnly("javax.annotation:jsr250-api:1.0")
|
compileOnly("javax.annotation:jsr250-api:1.0")
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
||||||
implementation("com.github.paolorotolo:appintro:4.1.0")
|
implementation("com.github.AppIntro:AppIntro:6.2.0")
|
||||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
implementation("com.google.dagger:dagger:$daggerVersion")
|
implementation("com.google.dagger:dagger:$daggerVersion")
|
||||||
implementation("com.google.guava:guava:30.1.1-android")
|
implementation("com.google.guava:guava:31.1-android")
|
||||||
implementation("io.ktor:ktor-client-android:$ktorVersion")
|
implementation("io.ktor:ktor-client-android:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-jackson:$ktorVersion")
|
implementation("io.ktor:ktor-client-jackson:$ktorVersion")
|
||||||
@@ -116,11 +116,11 @@ dependencies {
|
|||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kxCoroutinesVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kxCoroutinesVersion")
|
||||||
implementation("androidx.appcompat:appcompat:1.3.1")
|
implementation("androidx.appcompat:appcompat:1.4.1")
|
||||||
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
|
implementation("androidx.legacy:legacy-preference-v14:1.0.0")
|
||||||
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
implementation("androidx.legacy:legacy-support-v4:1.0.0")
|
||||||
implementation("com.google.android.material:material:1.4.0")
|
implementation("com.google.android.material:material:1.5.0")
|
||||||
implementation("com.opencsv:opencsv:5.5.1")
|
implementation("com.opencsv:opencsv:5.6")
|
||||||
implementation(project(":uhabits-core"))
|
implementation(project(":uhabits-core"))
|
||||||
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
|
kapt("com.google.dagger:dagger-compiler:$daggerVersion")
|
||||||
kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion")
|
kaptAndroidTest("com.google.dagger:dagger-compiler:$daggerVersion")
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -55,6 +55,7 @@ import org.junit.runner.RunWith
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class HabitsTest : BaseUserInterfaceTest() {
|
class HabitsTest : BaseUserInterfaceTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun shouldCreateHabit() {
|
fun shouldCreateHabit() {
|
||||||
@@ -180,6 +181,8 @@ class HabitsTest : BaseUserInterfaceTest() {
|
|||||||
longPressCheckmarks("Wake up early", count = 2)
|
longPressCheckmarks("Wake up early", count = 2)
|
||||||
clickText("Wake up early")
|
clickText("Wake up early")
|
||||||
verifyShowsScreen(SHOW_HABIT)
|
verifyShowsScreen(SHOW_HABIT)
|
||||||
|
// TODO: find a better way than sleeping in tests
|
||||||
|
Thread.sleep(2001L)
|
||||||
verifyDisplaysText("10%")
|
verifyDisplaysText("10%")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +197,8 @@ class HabitsTest : BaseUserInterfaceTest() {
|
|||||||
verifyDoesNotDisplayText("Track time")
|
verifyDoesNotDisplayText("Track time")
|
||||||
verifyDisplaysText("Wake up early")
|
verifyDisplaysText("Wake up early")
|
||||||
longPressCheckmarks("Wake up early", count = 1)
|
longPressCheckmarks("Wake up early", count = 1)
|
||||||
|
// TODO: find a better way than sleeping in tests
|
||||||
|
Thread.sleep(2001L)
|
||||||
verifyDoesNotDisplayText("Wake up early")
|
verifyDoesNotDisplayText("Wake up early")
|
||||||
clickMenu(TOGGLE_COMPLETED)
|
clickMenu(TOGGLE_COMPLETED)
|
||||||
verifyDisplaysText("Track time")
|
verifyDisplaysText("Track time")
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.test.filters.MediumTest
|
|||||||
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
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.utils.PaletteUtils
|
import org.isoron.uhabits.utils.PaletteUtils
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -42,6 +43,7 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
super.setUp()
|
super.setUp()
|
||||||
view = component.getNumberButtonViewFactory().create().apply {
|
view = component.getNumberButtonViewFactory().create().apply {
|
||||||
units = "steps"
|
units = "steps"
|
||||||
|
targetType = NumericalHabitType.AT_LEAST
|
||||||
threshold = 100.0
|
threshold = 100.0
|
||||||
color = PaletteUtils.getAndroidTestColor(8)
|
color = PaletteUtils.getAndroidTestColor(8)
|
||||||
onEdit = { edited = true }
|
onEdit = { edited = true }
|
||||||
@@ -74,10 +76,10 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRender_emptyUnits() {
|
fun testRender_atMostAboveThreshold() {
|
||||||
view.value = 500.0
|
view.value = 500.0
|
||||||
view.units = ""
|
view.targetType = NumericalHabitType.AT_MOST
|
||||||
assertRenders(view, "$PATH/render_unitless.png")
|
assertRenders(view, "$PATH/render_at_most_above.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -86,6 +88,13 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
assertRenders(view, "$PATH/render_below.png")
|
assertRenders(view, "$PATH/render_below.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRender_atMostBetweenThresholds() {
|
||||||
|
view.value = 110.0
|
||||||
|
view.targetType = NumericalHabitType.AT_MOST
|
||||||
|
assertRenders(view, "$PATH/render_at_most_between.png")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRender_zero() {
|
fun testRender_zero() {
|
||||||
view.value = 0.0
|
view.value = 0.0
|
||||||
@@ -93,15 +102,21 @@ class NumberButtonViewTest : BaseViewTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClick_shortToggleDisabled() {
|
fun testRender_atMostBelowThreshold() {
|
||||||
prefs.isShortToggleEnabled = false
|
view.value = 0.0
|
||||||
view.performClick()
|
view.targetType = NumericalHabitType.AT_MOST
|
||||||
assertFalse(edited)
|
assertRenders(view, "$PATH/render_at_most_below.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testClick_shortToggleEnabled() {
|
fun testRender_emptyUnits() {
|
||||||
prefs.isShortToggleEnabled = true
|
view.value = 500.0
|
||||||
|
view.units = ""
|
||||||
|
assertRenders(view, "$PATH/render_unitless.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClick() {
|
||||||
view.performClick()
|
view.performClick()
|
||||||
assertTrue(edited)
|
assertTrue(edited)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.test.filters.MediumTest
|
|||||||
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
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.utils.PaletteUtils
|
import org.isoron.uhabits.utils.PaletteUtils
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
@@ -55,6 +56,7 @@ class NumberPanelViewTest : BaseViewTest() {
|
|||||||
buttonCount = 4
|
buttonCount = 4
|
||||||
color = PaletteUtils.getAndroidTestColor(7)
|
color = PaletteUtils.getAndroidTestColor(7)
|
||||||
units = "steps"
|
units = "steps"
|
||||||
|
targetType = NumericalHabitType.AT_LEAST
|
||||||
threshold = 5000.0
|
threshold = 5000.0
|
||||||
}
|
}
|
||||||
view.onAttachedToWindow()
|
view.onAttachedToWindow()
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ 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),
|
||||||
unit = "",
|
|
||||||
targetValue = 0.0,
|
|
||||||
theme = LightTheme(),
|
theme = LightTheme(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class PerformanceTest : BaseAndroidTest() {
|
|||||||
val habit = fixtures.createEmptyHabit()
|
val habit = fixtures.createEmptyHabit()
|
||||||
for (i in 0..4999) {
|
for (i in 0..4999) {
|
||||||
val timestamp: Timestamp = Timestamp(i * DAY_LENGTH)
|
val timestamp: Timestamp = Timestamp(i * DAY_LENGTH)
|
||||||
CreateRepetitionCommand(habitList, habit, timestamp, 1).run()
|
CreateRepetitionCommand(habitList, habit, timestamp, 1, "").run()
|
||||||
}
|
}
|
||||||
db.setTransactionSuccessful()
|
db.setTransactionSuccessful()
|
||||||
db.endTransaction()
|
db.endTransaction()
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import android.widget.FrameLayout
|
|||||||
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.hamcrest.CoreMatchers
|
import org.hamcrest.CoreMatchers
|
||||||
import org.hamcrest.CoreMatchers.`is`
|
|
||||||
import org.hamcrest.CoreMatchers.equalTo
|
import org.hamcrest.CoreMatchers.equalTo
|
||||||
|
import org.hamcrest.CoreMatchers.`is`
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.isoron.uhabits.BaseViewTest
|
import org.isoron.uhabits.BaseViewTest
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
|||||||
@@ -17,9 +17,10 @@
|
|||||||
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="org.isoron.uhabits">
|
package="org.isoron.uhabits">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
|
||||||
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
@@ -48,11 +49,11 @@
|
|||||||
android:name=".activities.habits.list.ListHabitsActivity"
|
android:name=".activities.habits.list.ListHabitsActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/main_activity_title"
|
android:label="@string/main_activity_title"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop" />
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/main_activity_title"
|
android:label="@string/main_activity_title"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:targetActivity=".activities.habits.list.ListHabitsActivity">
|
android:targetActivity=".activities.habits.list.ListHabitsActivity">
|
||||||
@@ -85,6 +86,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.HabitPickerDialog"
|
android:name=".widgets.activities.HabitPickerDialog"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
@@ -93,6 +95,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.BooleanHabitPickerDialog"
|
android:name=".widgets.activities.BooleanHabitPickerDialog"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
@@ -101,6 +104,7 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.NumericalHabitPickerDialog"
|
android:name=".widgets.activities.NumericalHabitPickerDialog"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
@@ -117,9 +121,10 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".widgets.activities.NumericalCheckmarkWidgetActivity"
|
android:name=".widgets.activities.NumericalCheckmarkWidgetActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
android:label="NumericalCheckmarkWidget"
|
android:label="NumericalCheckmarkWidget"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
android:theme="@style/Theme.AppCompat.Light.Dialog">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.isoron.uhabits.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY" />
|
<action android:name="org.isoron.uhabits.ACTION_SHOW_NUMERICAL_VALUE_ACTIVITY" />
|
||||||
@@ -128,13 +133,14 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".notifications.SnoozeDelayPickerActivity"
|
android:name=".notifications.SnoozeDelayPickerActivity"
|
||||||
android:taskAffinity=""
|
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
|
android:taskAffinity=""
|
||||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.CheckmarkWidgetProvider"
|
android:name=".widgets.CheckmarkWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/checkmark">
|
android:label="@string/checkmark">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -152,6 +158,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.HistoryWidgetProvider"
|
android:name=".widgets.HistoryWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/history">
|
android:label="@string/history">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -164,6 +171,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.ScoreWidgetProvider"
|
android:name=".widgets.ScoreWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/score">
|
android:label="@string/score">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -176,6 +184,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.StreakWidgetProvider"
|
android:name=".widgets.StreakWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/streaks">
|
android:label="@string/streaks">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -188,6 +197,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.FrequencyWidgetProvider"
|
android:name=".widgets.FrequencyWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/frequency">
|
android:label="@string/frequency">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -200,6 +210,7 @@
|
|||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widgets.TargetWidgetProvider"
|
android:name=".widgets.TargetWidgetProvider"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/target">
|
android:label="@string/target">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
@@ -210,13 +221,17 @@
|
|||||||
android:resource="@xml/widget_target_info" />
|
android:resource="@xml/widget_target_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receivers.ReminderReceiver">
|
<receiver
|
||||||
|
android:name=".receivers.ReminderReceiver"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".receivers.WidgetReceiver">
|
<receiver android:name=".receivers.WidgetReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<action android:name="org.isoron.uhabits.ACTION_SET_NUMERICAL_VALUE" />
|
<action android:name="org.isoron.uhabits.ACTION_SET_NUMERICAL_VALUE" />
|
||||||
@@ -267,7 +282,7 @@
|
|||||||
<!-- Locale/Tasker -->
|
<!-- Locale/Tasker -->
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".automation.FireSettingReceiver"
|
android:name=".automation.FireSettingReceiver"
|
||||||
android:exported="true">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -49,23 +49,12 @@ class AndroidDataView(
|
|||||||
override fun onShowPress(e: MotionEvent?) = Unit
|
override fun onShowPress(e: MotionEvent?) = Unit
|
||||||
|
|
||||||
override fun onSingleTapUp(e: MotionEvent?): Boolean {
|
override fun onSingleTapUp(e: MotionEvent?): Boolean {
|
||||||
val x: Float
|
return handleClick(e, true)
|
||||||
val y: Float
|
|
||||||
try {
|
|
||||||
val pointerId = e!!.getPointerId(0)
|
|
||||||
x = e.getX(pointerId)
|
|
||||||
y = e.getY(pointerId)
|
|
||||||
} catch (ex: RuntimeException) {
|
|
||||||
// Android often throws IllegalArgumentException here. Apparently,
|
|
||||||
// the pointer id may become invalid shortly after calling
|
|
||||||
// e.getPointerId.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongPress(e: MotionEvent?) = Unit
|
override fun onLongPress(e: MotionEvent?) {
|
||||||
|
handleClick(e)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onScroll(
|
override fun onScroll(
|
||||||
e1: MotionEvent?,
|
e1: MotionEvent?,
|
||||||
@@ -137,4 +126,22 @@ class AndroidDataView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleClick(e: MotionEvent?, isSingleTap: Boolean = false): Boolean {
|
||||||
|
val x: Float
|
||||||
|
val y: Float
|
||||||
|
try {
|
||||||
|
val pointerId = e!!.getPointerId(0)
|
||||||
|
x = e.getX(pointerId)
|
||||||
|
y = e.getY(pointerId)
|
||||||
|
} catch (ex: RuntimeException) {
|
||||||
|
// Android often throws IllegalArgumentException here. Apparently,
|
||||||
|
// the pointer id may become invalid shortly after calling
|
||||||
|
// e.getPointerId.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (isSingleTap) view?.onClick(x / canvas.innerDensity, y / canvas.innerDensity)
|
||||||
|
else view?.onLongClick(x / canvas.innerDensity, y / canvas.innerDensity)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package org.isoron.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
||||||
|
import android.widget.Button
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import org.isoron.platform.gui.toInt
|
||||||
|
import org.isoron.platform.time.JavaLocalDateFormatter
|
||||||
|
import org.isoron.platform.time.LocalDate
|
||||||
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.NO
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.SKIP
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.UNKNOWN
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
||||||
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
|
import org.isoron.uhabits.core.ui.views.Theme
|
||||||
|
import org.isoron.uhabits.databinding.CheckmarkDialogBinding
|
||||||
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
|
import org.isoron.uhabits.utils.StyledResources
|
||||||
|
import java.util.Locale
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class CheckmarkDialog
|
||||||
|
@Inject constructor(
|
||||||
|
@ActivityContext private val context: Context,
|
||||||
|
private val preferences: Preferences,
|
||||||
|
) : View.OnClickListener {
|
||||||
|
|
||||||
|
private lateinit var binding: CheckmarkDialogBinding
|
||||||
|
private lateinit var fontAwesome: Typeface
|
||||||
|
private val allButtons = mutableListOf<Button>()
|
||||||
|
private var selectedButton: Button? = null
|
||||||
|
|
||||||
|
fun create(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
date: LocalDate,
|
||||||
|
paletteColor: PaletteColor,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback,
|
||||||
|
theme: Theme,
|
||||||
|
): AlertDialog {
|
||||||
|
binding = CheckmarkDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
|
fontAwesome = InterfaceUtils.getFontAwesome(context)!!
|
||||||
|
binding.etNotes.append(notes)
|
||||||
|
setUpButtons(selectedValue, theme.color(paletteColor).toInt())
|
||||||
|
|
||||||
|
val dialog = AlertDialog.Builder(context)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setTitle(JavaLocalDateFormatter(Locale.getDefault()).longFormat(date))
|
||||||
|
.setPositiveButton(R.string.save) { _, _ ->
|
||||||
|
val newValue = when (selectedButton?.id) {
|
||||||
|
R.id.yesBtn -> YES_MANUAL
|
||||||
|
R.id.noBtn -> NO
|
||||||
|
R.id.skippedBtn -> SKIP
|
||||||
|
else -> UNKNOWN
|
||||||
|
}
|
||||||
|
callback.onNotesSaved(newValue, binding.etNotes.text.toString())
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
|
callback.onNotesDismissed()
|
||||||
|
}
|
||||||
|
.setOnDismissListener {
|
||||||
|
callback.onNotesDismissed()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
|
||||||
|
dialog.setOnShowListener {
|
||||||
|
binding.etNotes.requestFocus()
|
||||||
|
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpButtons(value: Int, color: Int) {
|
||||||
|
val sres = StyledResources(context)
|
||||||
|
val mediumContrastColor = sres.getColor(R.attr.contrast60)
|
||||||
|
setButtonAttrs(binding.yesBtn, color)
|
||||||
|
setButtonAttrs(binding.noBtn, mediumContrastColor)
|
||||||
|
setButtonAttrs(binding.skippedBtn, color, visible = preferences.isSkipEnabled)
|
||||||
|
setButtonAttrs(binding.questionBtn, mediumContrastColor, visible = preferences.areQuestionMarksEnabled)
|
||||||
|
when (value) {
|
||||||
|
UNKNOWN -> if (preferences.areQuestionMarksEnabled) {
|
||||||
|
binding.questionBtn.performClick()
|
||||||
|
} else {
|
||||||
|
binding.noBtn.performClick()
|
||||||
|
}
|
||||||
|
SKIP -> binding.skippedBtn.performClick()
|
||||||
|
YES_MANUAL -> binding.yesBtn.performClick()
|
||||||
|
YES_AUTO, NO -> binding.noBtn.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setButtonAttrs(button: Button, color: Int, visible: Boolean = true) {
|
||||||
|
button.apply {
|
||||||
|
visibility = if (visible) View.VISIBLE else View.GONE
|
||||||
|
typeface = fontAwesome
|
||||||
|
setTextColor(color)
|
||||||
|
setOnClickListener(this@CheckmarkDialog)
|
||||||
|
}
|
||||||
|
allButtons.add(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
allButtons.forEach {
|
||||||
|
if (v?.id == it.id) {
|
||||||
|
it.isSelected = true
|
||||||
|
selectedButton = it
|
||||||
|
} else it.isSelected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,13 +68,12 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
contentView.everyDayRadioButton.setOnClickListener {
|
contentView.everyDayRadioButton.setOnClickListener {
|
||||||
check(contentView.everyDayRadioButton)
|
check(contentView.everyDayRadioButton)
|
||||||
unfocusAll()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.everyXDaysRadioButton.setOnClickListener {
|
contentView.everyXDaysRadioButton.setOnClickListener {
|
||||||
check(contentView.everyXDaysRadioButton)
|
check(contentView.everyXDaysRadioButton)
|
||||||
val everyXDaysTextView = contentView.everyXDaysTextView
|
val everyXDaysTextView = contentView.everyXDaysTextView
|
||||||
focus(everyXDaysTextView)
|
selectInputField(everyXDaysTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.everyXDaysTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
@@ -83,7 +82,7 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
contentView.xTimesPerWeekRadioButton.setOnClickListener {
|
contentView.xTimesPerWeekRadioButton.setOnClickListener {
|
||||||
check(contentView.xTimesPerWeekRadioButton)
|
check(contentView.xTimesPerWeekRadioButton)
|
||||||
focus(contentView.xTimesPerWeekTextView)
|
selectInputField(contentView.xTimesPerWeekTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.xTimesPerWeekTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
@@ -92,7 +91,7 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
contentView.xTimesPerMonthRadioButton.setOnClickListener {
|
contentView.xTimesPerMonthRadioButton.setOnClickListener {
|
||||||
check(contentView.xTimesPerMonthRadioButton)
|
check(contentView.xTimesPerMonthRadioButton)
|
||||||
focus(contentView.xTimesPerMonthTextView)
|
selectInputField(contentView.xTimesPerMonthTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.xTimesPerMonthTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
@@ -101,7 +100,7 @@ class FrequencyPickerDialog(
|
|||||||
|
|
||||||
contentView.xTimesPerYDaysRadioButton.setOnClickListener {
|
contentView.xTimesPerYDaysRadioButton.setOnClickListener {
|
||||||
check(contentView.xTimesPerYDaysRadioButton)
|
check(contentView.xTimesPerYDaysRadioButton)
|
||||||
focus(contentView.xTimesPerYDaysXTextView)
|
selectInputField(contentView.xTimesPerYDaysXTextView)
|
||||||
}
|
}
|
||||||
|
|
||||||
contentView.xTimesPerYDaysXTextView.setOnFocusChangeListener { v, hasFocus ->
|
contentView.xTimesPerYDaysXTextView.setOnFocusChangeListener { v, hasFocus ->
|
||||||
@@ -185,7 +184,7 @@ class FrequencyPickerDialog(
|
|||||||
if (freqDenominator == 30 || freqDenominator == 31) {
|
if (freqDenominator == 30 || freqDenominator == 31) {
|
||||||
contentView.xTimesPerMonthRadioButton.isChecked = true
|
contentView.xTimesPerMonthRadioButton.isChecked = true
|
||||||
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
|
contentView.xTimesPerMonthTextView.setText(freqNumerator.toString())
|
||||||
focus(contentView.xTimesPerMonthTextView)
|
selectInputField(contentView.xTimesPerMonthTextView)
|
||||||
} else {
|
} else {
|
||||||
if (freqNumerator == 1) {
|
if (freqNumerator == 1) {
|
||||||
if (freqDenominator == 1) {
|
if (freqDenominator == 1) {
|
||||||
@@ -193,13 +192,13 @@ class FrequencyPickerDialog(
|
|||||||
} else {
|
} else {
|
||||||
contentView.everyXDaysRadioButton.isChecked = true
|
contentView.everyXDaysRadioButton.isChecked = true
|
||||||
contentView.everyXDaysTextView.setText(freqDenominator.toString())
|
contentView.everyXDaysTextView.setText(freqDenominator.toString())
|
||||||
focus(contentView.everyXDaysTextView)
|
selectInputField(contentView.everyXDaysTextView)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (freqDenominator == 7) {
|
if (freqDenominator == 7) {
|
||||||
contentView.xTimesPerWeekRadioButton.isChecked = true
|
contentView.xTimesPerWeekRadioButton.isChecked = true
|
||||||
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
contentView.xTimesPerWeekTextView.setText(freqNumerator.toString())
|
||||||
focus(contentView.xTimesPerWeekTextView)
|
selectInputField(contentView.xTimesPerWeekTextView)
|
||||||
} else {
|
} else {
|
||||||
contentView.xTimesPerYDaysRadioButton.isChecked = true
|
contentView.xTimesPerYDaysRadioButton.isChecked = true
|
||||||
contentView.xTimesPerYDaysXTextView.setText(freqNumerator.toString())
|
contentView.xTimesPerYDaysXTextView.setText(freqNumerator.toString())
|
||||||
@@ -209,8 +208,7 @@ class FrequencyPickerDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun focus(view: EditText) {
|
private fun selectInputField(view: EditText) {
|
||||||
view.requestFocus()
|
|
||||||
view.setSelection(view.text.length)
|
view.setSelection(view.text.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,10 +219,4 @@ class FrequencyPickerDialog(
|
|||||||
contentView.xTimesPerMonthRadioButton.isChecked = false
|
contentView.xTimesPerMonthRadioButton.isChecked = false
|
||||||
contentView.xTimesPerYDaysRadioButton.isChecked = false
|
contentView.xTimesPerYDaysRadioButton.isChecked = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unfocusAll() {
|
|
||||||
contentView.everyXDaysTextView.clearFocus()
|
|
||||||
contentView.xTimesPerWeekTextView.clearFocus()
|
|
||||||
contentView.xTimesPerMonthTextView.clearFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,11 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
firstWeekday = preferences.firstWeekday,
|
firstWeekday = preferences.firstWeekday,
|
||||||
paletteColor = habit.color,
|
paletteColor = habit.color,
|
||||||
series = emptyList(),
|
series = emptyList(),
|
||||||
|
defaultSquare = HistoryChart.Square.OFF,
|
||||||
|
notesIndicators = emptyList(),
|
||||||
theme = themeSwitcher.currentTheme,
|
theme = themeSwitcher.currentTheme,
|
||||||
today = DateUtils.getTodayWithOffset().toLocalDate(),
|
today = DateUtils.getTodayWithOffset().toLocalDate(),
|
||||||
onDateClickedListener = onDateClickedListener ?: OnDateClickedListener { },
|
onDateClickedListener = onDateClickedListener ?: object : OnDateClickedListener {},
|
||||||
padding = 10.0,
|
padding = 10.0,
|
||||||
)
|
)
|
||||||
dataView = AndroidDataView(context!!, null)
|
dataView = AndroidDataView(context!!, null)
|
||||||
@@ -101,6 +103,8 @@ class HistoryEditorDialog : AppCompatDialogFragment(), CommandRunner.Listener {
|
|||||||
theme = LightTheme()
|
theme = LightTheme()
|
||||||
)
|
)
|
||||||
chart?.series = model.series
|
chart?.series = model.series
|
||||||
|
chart?.defaultSquare = model.defaultSquare
|
||||||
|
chart?.notesIndicators = model.notesIndicators
|
||||||
dataView.postInvalidate()
|
dataView.postInvalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,16 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.activities.common.dialogs
|
package org.isoron.uhabits.activities.common.dialogs
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
|
import android.text.Spanned
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.NumberPicker
|
import android.widget.NumberPicker
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@@ -33,6 +37,7 @@ import org.isoron.uhabits.R
|
|||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
|
import java.text.DecimalFormatSymbols
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
@@ -40,18 +45,39 @@ class NumberPickerFactory
|
|||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
@ActivityContext private val context: Context
|
@ActivityContext private val context: Context
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
fun create(
|
fun create(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
): AlertDialog {
|
): AlertDialog {
|
||||||
|
|
||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
val view = inflater.inflate(R.layout.number_picker_dialog, null)
|
val view = inflater.inflate(R.layout.number_picker_dialog, null)
|
||||||
|
|
||||||
val picker = view.findViewById<NumberPicker>(R.id.picker)
|
val picker = view.findViewById<NumberPicker>(R.id.picker)
|
||||||
val picker2 = view.findViewById<NumberPicker>(R.id.picker2)
|
val picker2 = view.findViewById<NumberPicker>(R.id.picker2)
|
||||||
val tvUnit = view.findViewById<TextView>(R.id.tvUnit)
|
val etNotes = view.findViewById<EditText>(R.id.etNotes)
|
||||||
|
|
||||||
|
// Install filter to intercept decimal separator before it is parsed
|
||||||
|
val watcherFilter: InputFilter = SeparatorWatcherInputFilter(picker2)
|
||||||
|
val pickerInputText = getNumberPickerInputText(picker)
|
||||||
|
pickerInputText.filters = arrayOf(watcherFilter).plus(pickerInputText.filters)
|
||||||
|
|
||||||
|
// Install custom focus listener to replace "5" by "50" instead of "05"
|
||||||
|
val picker2InputText = getNumberPickerInputText(picker2)
|
||||||
|
val prevFocusChangeListener = picker2InputText.onFocusChangeListener
|
||||||
|
picker2InputText.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
|
||||||
|
val str = picker2InputText.text.toString()
|
||||||
|
if (str.length == 1) picker2InputText.setText("${str}0")
|
||||||
|
prevFocusChangeListener.onFocusChange(v, hasFocus)
|
||||||
|
}
|
||||||
|
|
||||||
|
view.findViewById<TextView>(R.id.tvUnit).text = unit
|
||||||
|
view.findViewById<TextView>(R.id.tvSeparator).text =
|
||||||
|
DecimalFormatSymbols.getInstance().decimalSeparator.toString()
|
||||||
|
|
||||||
val intValue = (value * 100).roundToLong().toInt()
|
val intValue = (value * 100).roundToLong().toInt()
|
||||||
|
|
||||||
@@ -61,20 +87,23 @@ class NumberPickerFactory
|
|||||||
picker.wrapSelectorWheel = false
|
picker.wrapSelectorWheel = false
|
||||||
|
|
||||||
picker2.minValue = 0
|
picker2.minValue = 0
|
||||||
picker2.maxValue = 19
|
picker2.maxValue = 99
|
||||||
picker2.setFormatter { v -> String.format("%02d", 5 * v) }
|
picker2.setFormatter { v -> String.format("%02d", v) }
|
||||||
picker2.value = intValue % 100 / 5
|
picker2.value = intValue % 100
|
||||||
refreshInitialValue(picker2)
|
|
||||||
|
|
||||||
tvUnit.text = unit
|
|
||||||
|
|
||||||
|
etNotes.setText(notes)
|
||||||
val dialog = AlertDialog.Builder(context)
|
val dialog = AlertDialog.Builder(context)
|
||||||
.setView(view)
|
.setView(view)
|
||||||
.setTitle(R.string.change_value)
|
.setTitle(dateString)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.save) { _, _ ->
|
||||||
picker.clearFocus()
|
picker.clearFocus()
|
||||||
val v = picker.value + 0.05 * picker2.value
|
picker2.clearFocus()
|
||||||
callback.onNumberPicked(v)
|
val v = picker.value + 0.01 * picker2.value
|
||||||
|
val note = etNotes.text.toString()
|
||||||
|
callback.onNumberPicked(v, note)
|
||||||
|
}
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
|
callback.onNumberPickerDismissed()
|
||||||
}
|
}
|
||||||
.setOnDismissListener {
|
.setOnDismissListener {
|
||||||
callback.onNumberPickerDismissed()
|
callback.onNumberPickerDismissed()
|
||||||
@@ -82,27 +111,63 @@ class NumberPickerFactory
|
|||||||
.create()
|
.create()
|
||||||
|
|
||||||
dialog.setOnShowListener {
|
dialog.setOnShowListener {
|
||||||
picker.getChildAt(0)?.requestFocus()
|
showSoftInput(dialog, pickerInputText)
|
||||||
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InterfaceUtils.setupEditorAction(
|
InterfaceUtils.setupEditorAction(
|
||||||
picker
|
picker
|
||||||
) { _, actionId, _ ->
|
) { _, actionId, _ ->
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE)
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
InterfaceUtils.setupEditorAction(
|
||||||
|
picker2
|
||||||
|
) { _, actionId, _ ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
|
||||||
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshInitialValue(picker: NumberPicker) {
|
@SuppressLint("DiscouragedPrivateApi")
|
||||||
// Workaround for Android bug:
|
private fun getNumberPickerInputText(picker: NumberPicker): EditText {
|
||||||
// https://code.google.com/p/android/issues/detail?id=35482
|
|
||||||
val f = NumberPicker::class.java.getDeclaredField("mInputText")
|
val f = NumberPicker::class.java.getDeclaredField("mInputText")
|
||||||
f.isAccessible = true
|
f.isAccessible = true
|
||||||
val inputText = f.get(picker) as EditText
|
return f.get(picker) as EditText
|
||||||
inputText.filters = arrayOfNulls<InputFilter>(0)
|
}
|
||||||
|
|
||||||
|
private fun showSoftInput(dialog: AlertDialog, v: View) {
|
||||||
|
dialog.window?.setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
|
v.requestFocus()
|
||||||
|
val inputMethodManager = context.getSystemService(InputMethodManager::class.java)
|
||||||
|
inputMethodManager?.showSoftInput(v, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SeparatorWatcherInputFilter(private val nextPicker: NumberPicker) : InputFilter {
|
||||||
|
override fun filter(
|
||||||
|
source: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
end: Int,
|
||||||
|
dest: Spanned?,
|
||||||
|
dstart: Int,
|
||||||
|
dend: Int
|
||||||
|
): CharSequence {
|
||||||
|
if (source == null || source.isEmpty()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for (c in source) {
|
||||||
|
if (c == DecimalFormatSymbols.getInstance().decimalSeparator || c == '.' || c == ',') {
|
||||||
|
nextPicker.performLongClick()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
var reminderHour = -1
|
var reminderHour = -1
|
||||||
var reminderMin = -1
|
var reminderMin = -1
|
||||||
var reminderDays: WeekdayList = WeekdayList.EVERY_DAY
|
var reminderDays: WeekdayList = WeekdayList.EVERY_DAY
|
||||||
|
var targetType = NumericalHabitType.AT_LEAST
|
||||||
|
|
||||||
override fun onCreate(state: Bundle?) {
|
override fun onCreate(state: Bundle?) {
|
||||||
super.onCreate(state)
|
super.onCreate(state)
|
||||||
@@ -107,6 +108,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
color = habit.color
|
color = habit.color
|
||||||
freqNum = habit.frequency.numerator
|
freqNum = habit.frequency.numerator
|
||||||
freqDen = habit.frequency.denominator
|
freqDen = habit.frequency.denominator
|
||||||
|
targetType = habit.targetType
|
||||||
habit.reminder?.let {
|
habit.reminder?.let {
|
||||||
reminderHour = it.hour
|
reminderHour = it.hour
|
||||||
reminderMin = it.minute
|
reminderMin = it.minute
|
||||||
@@ -138,6 +140,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
HabitType.YES_NO -> {
|
HabitType.YES_NO -> {
|
||||||
binding.unitOuterBox.visibility = View.GONE
|
binding.unitOuterBox.visibility = View.GONE
|
||||||
binding.targetOuterBox.visibility = View.GONE
|
binding.targetOuterBox.visibility = View.GONE
|
||||||
|
binding.targetTypeOuterBox.visibility = View.GONE
|
||||||
}
|
}
|
||||||
HabitType.NUMERICAL -> {
|
HabitType.NUMERICAL -> {
|
||||||
binding.nameInput.hint = getString(R.string.measurable_short_example)
|
binding.nameInput.hint = getString(R.string.measurable_short_example)
|
||||||
@@ -172,6 +175,23 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
dialog.show(supportFragmentManager, "frequencyPicker")
|
dialog.show(supportFragmentManager, "frequencyPicker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
populateTargetType()
|
||||||
|
binding.targetTypePicker.setOnClickListener {
|
||||||
|
val builder = AlertDialog.Builder(this)
|
||||||
|
val arrayAdapter = ArrayAdapter<String>(this, android.R.layout.select_dialog_item)
|
||||||
|
arrayAdapter.add(getString(R.string.target_type_at_least))
|
||||||
|
arrayAdapter.add(getString(R.string.target_type_at_most))
|
||||||
|
builder.setAdapter(arrayAdapter) { dialog, which ->
|
||||||
|
targetType = when (which) {
|
||||||
|
0 -> NumericalHabitType.AT_LEAST
|
||||||
|
else -> NumericalHabitType.AT_MOST
|
||||||
|
}
|
||||||
|
populateTargetType()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
builder.show()
|
||||||
|
}
|
||||||
|
|
||||||
binding.numericalFrequencyPicker.setOnClickListener {
|
binding.numericalFrequencyPicker.setOnClickListener {
|
||||||
val builder = AlertDialog.Builder(this)
|
val builder = AlertDialog.Builder(this)
|
||||||
val arrayAdapter = ArrayAdapter<String>(this, android.R.layout.select_dialog_item)
|
val arrayAdapter = ArrayAdapter<String>(this, android.R.layout.select_dialog_item)
|
||||||
@@ -262,7 +282,7 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
habit.frequency = Frequency(freqNum, freqDen)
|
habit.frequency = Frequency(freqNum, freqDen)
|
||||||
if (habitType == HabitType.NUMERICAL) {
|
if (habitType == HabitType.NUMERICAL) {
|
||||||
habit.targetValue = targetInput.text.toString().toDouble()
|
habit.targetValue = targetInput.text.toString().toDouble()
|
||||||
habit.targetType = NumericalHabitType.AT_LEAST
|
habit.targetType = targetType
|
||||||
habit.unit = unitInput.text.trim().toString()
|
habit.unit = unitInput.text.trim().toString()
|
||||||
}
|
}
|
||||||
habit.type = habitType
|
habit.type = habitType
|
||||||
@@ -324,6 +344,13 @@ class EditHabitActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun populateTargetType() {
|
||||||
|
binding.targetTypePicker.text = when (targetType) {
|
||||||
|
NumericalHabitType.AT_MOST -> getString(R.string.target_type_at_most)
|
||||||
|
else -> getString(R.string.target_type_at_least)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateColors() {
|
private fun updateColors() {
|
||||||
androidColor = themeSwitcher.currentTheme.color(color).toInt()
|
androidColor = themeSwitcher.currentTheme.color(color).toInt()
|
||||||
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
|
binding.colorButton.backgroundTintList = ColorStateList.valueOf(androidColor)
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
|
|||||||
|
|
||||||
override fun onQuestionMarksChanged() {
|
override fun onQuestionMarksChanged() {
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
|
menu.behavior.onPreferencesChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class ListHabitsMenu @Inject constructor(
|
|||||||
@ActivityContext context: Context,
|
@ActivityContext context: Context,
|
||||||
private val preferences: Preferences,
|
private val preferences: Preferences,
|
||||||
private val themeSwitcher: ThemeSwitcher,
|
private val themeSwitcher: ThemeSwitcher,
|
||||||
private val behavior: ListHabitsMenuBehavior
|
val behavior: ListHabitsMenuBehavior
|
||||||
) {
|
) {
|
||||||
val activity = (context as AppCompatActivity)
|
val activity = (context as AppCompatActivity)
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import dagger.Lazy
|
import dagger.Lazy
|
||||||
|
import org.isoron.platform.time.LocalDate
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||||
@@ -89,6 +91,7 @@ class ListHabitsScreen
|
|||||||
private val importTaskFactory: ImportDataTaskFactory,
|
private val importTaskFactory: ImportDataTaskFactory,
|
||||||
private val colorPickerFactory: ColorPickerDialogFactory,
|
private val colorPickerFactory: ColorPickerDialogFactory,
|
||||||
private val numberPickerFactory: NumberPickerFactory,
|
private val numberPickerFactory: NumberPickerFactory,
|
||||||
|
private val checkMarkDialog: CheckmarkDialog,
|
||||||
private val behavior: Lazy<ListHabitsBehavior>
|
private val behavior: Lazy<ListHabitsBehavior>
|
||||||
) : CommandRunner.Listener,
|
) : CommandRunner.Listener,
|
||||||
ListHabitsBehavior.Screen,
|
ListHabitsBehavior.Screen,
|
||||||
@@ -225,9 +228,29 @@ class ListHabitsScreen
|
|||||||
override fun showNumberPicker(
|
override fun showNumberPicker(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback
|
callback: ListHabitsBehavior.NumberPickerCallback
|
||||||
) {
|
) {
|
||||||
numberPickerFactory.create(value, unit, callback).show()
|
numberPickerFactory.create(value, unit, notes, dateString, callback).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCheckmarkDialog(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
date: LocalDate,
|
||||||
|
dateString: String,
|
||||||
|
color: PaletteColor,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
|
) {
|
||||||
|
checkMarkDialog.create(
|
||||||
|
selectedValue,
|
||||||
|
notes,
|
||||||
|
date,
|
||||||
|
color,
|
||||||
|
callback,
|
||||||
|
themeSwitcher.currentTheme!!,
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExecuteString(command: Command): String? {
|
private fun getExecuteString(command: Command): String? {
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
|
|||||||
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.utils.dim
|
import org.isoron.uhabits.utils.drawNotesIndicator
|
||||||
import org.isoron.uhabits.utils.getFontAwesome
|
import org.isoron.uhabits.utils.getFontAwesome
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.sp
|
||||||
import org.isoron.uhabits.utils.sres
|
import org.isoron.uhabits.utils.sres
|
||||||
import org.isoron.uhabits.utils.toMeasureSpec
|
import org.isoron.uhabits.utils.toMeasureSpec
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -71,7 +71,15 @@ class CheckmarkButtonView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasNotes = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
var onToggle: (Int) -> Unit = {}
|
var onToggle: (Int) -> Unit = {}
|
||||||
|
|
||||||
|
var onEdit: () -> Unit = {}
|
||||||
private var drawer = Drawer()
|
private var drawer = Drawer()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -93,11 +101,12 @@ class CheckmarkButtonView(
|
|||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
if (preferences.isShortToggleEnabled) performToggle()
|
if (preferences.isShortToggleEnabled) performToggle()
|
||||||
else showMessage(resources.getString(R.string.long_press_to_toggle))
|
else onEdit()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean {
|
override fun onLongClick(v: View): Boolean {
|
||||||
performToggle()
|
if (preferences.isShortToggleEnabled) onEdit()
|
||||||
|
else performToggle()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +154,11 @@ class CheckmarkButtonView(
|
|||||||
}
|
}
|
||||||
else -> R.string.fa_check
|
else -> R.string.fa_check
|
||||||
}
|
}
|
||||||
|
paint.textSize = when {
|
||||||
|
id == R.string.fa_question -> sp(12.0f)
|
||||||
|
value == YES_AUTO -> sp(13.0f)
|
||||||
|
else -> sp(14.0f)
|
||||||
|
}
|
||||||
if (value == YES_AUTO) {
|
if (value == YES_AUTO) {
|
||||||
paint.strokeWidth = 5f
|
paint.strokeWidth = 5f
|
||||||
paint.style = Paint.Style.STROKE
|
paint.style = Paint.Style.STROKE
|
||||||
@@ -153,11 +167,6 @@ class CheckmarkButtonView(
|
|||||||
paint.style = Paint.Style.FILL
|
paint.style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
paint.textSize = when (id) {
|
|
||||||
UNKNOWN -> dim(R.dimen.smallerTextSize)
|
|
||||||
else -> dim(R.dimen.smallTextSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
val label = resources.getString(id)
|
val label = resources.getString(id)
|
||||||
val em = paint.measureText("m")
|
val em = paint.measureText("m")
|
||||||
|
|
||||||
@@ -170,6 +179,8 @@ class CheckmarkButtonView(
|
|||||||
paint.style = Paint.Style.FILL
|
paint.style = Paint.Style.FILL
|
||||||
canvas.drawText(label, rect.centerX(), rect.centerY(), paint)
|
canvas.drawText(label, rect.centerX(), rect.centerY(), paint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawNotesIndicator(canvas, color, em, hasNotes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,12 +54,24 @@ class CheckmarkPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notesIndicators = BooleanArray(0)
|
||||||
|
set(values) {
|
||||||
|
field = values
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
var onToggle: (Timestamp, Int) -> Unit = { _, _ -> }
|
var onToggle: (Timestamp, Int) -> Unit = { _, _ -> }
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var onEdit: (Timestamp) -> Unit = {}
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
override fun createButton(): CheckmarkButtonView = buttonFactory.create()
|
override fun createButton(): CheckmarkButtonView = buttonFactory.create()
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -72,8 +84,13 @@ class CheckmarkPanelView(
|
|||||||
index + dataOffset < values.size -> values[index + dataOffset]
|
index + dataOffset < values.size -> values[index + dataOffset]
|
||||||
else -> UNKNOWN
|
else -> UNKNOWN
|
||||||
}
|
}
|
||||||
|
button.hasNotes = when {
|
||||||
|
index + dataOffset < notesIndicators.size -> notesIndicators[index + dataOffset]
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
button.onToggle = { value -> onToggle(timestamp, value) }
|
button.onToggle = { value -> onToggle(timestamp, value) }
|
||||||
|
button.onEdit = { onEdit(timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,8 +124,9 @@ class HabitCardListAdapter @Inject constructor(
|
|||||||
val habit = cache.getHabitByPosition(position)
|
val habit = cache.getHabitByPosition(position)
|
||||||
val score = cache.getScore(habit!!.id!!)
|
val score = cache.getScore(habit!!.id!!)
|
||||||
val checkmarks = cache.getCheckmarks(habit.id!!)
|
val checkmarks = cache.getCheckmarks(habit.id!!)
|
||||||
|
val notesIndicators = cache.getNoteIndicators(habit.id!!)
|
||||||
val selected = selected.contains(habit)
|
val selected = selected.contains(habit)
|
||||||
listView!!.bindCardView(holder, habit, score, checkmarks, selected)
|
listView!!.bindCardView(holder, habit, score, checkmarks, notesIndicators, selected)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
|
override fun onViewAttachedToWindow(holder: HabitCardViewHolder) {
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class HabitCardListView(
|
|||||||
habit: Habit,
|
habit: Habit,
|
||||||
score: Double,
|
score: Double,
|
||||||
checkmarks: IntArray,
|
checkmarks: IntArray,
|
||||||
|
notesIndicators: BooleanArray,
|
||||||
selected: Boolean
|
selected: Boolean
|
||||||
): View {
|
): View {
|
||||||
val cardView = holder.itemView as HabitCardView
|
val cardView = holder.itemView as HabitCardView
|
||||||
@@ -98,6 +99,7 @@ class HabitCardListView(
|
|||||||
cardView.score = score
|
cardView.score = score
|
||||||
cardView.unit = habit.unit
|
cardView.unit = habit.unit
|
||||||
cardView.threshold = habit.targetValue / habit.frequency.denominator
|
cardView.threshold = habit.targetValue / habit.frequency.denominator
|
||||||
|
cardView.notesIndicators = notesIndicators
|
||||||
|
|
||||||
val detector = GestureDetector(context, CardViewGestureDetector(holder))
|
val detector = GestureDetector(context, CardViewGestureDetector(holder))
|
||||||
cardView.setOnTouchListener { _, ev ->
|
cardView.setOnTouchListener { _, ev ->
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ class HabitCardViewFactory
|
|||||||
fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior)
|
fun create() = HabitCardView(context, checkmarkPanelFactory, numberPanelFactory, behavior)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class DelayedToggle(
|
||||||
|
var habit: Habit,
|
||||||
|
var timestamp: Timestamp,
|
||||||
|
var value: Int
|
||||||
|
)
|
||||||
|
|
||||||
class HabitCardView(
|
class HabitCardView(
|
||||||
@ActivityContext context: Context,
|
@ActivityContext context: Context,
|
||||||
checkmarkPanelFactory: CheckmarkPanelViewFactory,
|
checkmarkPanelFactory: CheckmarkPanelViewFactory,
|
||||||
@@ -115,12 +121,22 @@ class HabitCardView(
|
|||||||
numberPanel.threshold = value
|
numberPanel.threshold = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notesIndicators
|
||||||
|
get() = checkmarkPanel.notesIndicators
|
||||||
|
set(values) {
|
||||||
|
checkmarkPanel.notesIndicators = values
|
||||||
|
numberPanel.notesIndicators = values
|
||||||
|
}
|
||||||
|
|
||||||
var checkmarkPanel: CheckmarkPanelView
|
var checkmarkPanel: CheckmarkPanelView
|
||||||
private var numberPanel: NumberPanelView
|
private var numberPanel: NumberPanelView
|
||||||
private var innerFrame: LinearLayout
|
private var innerFrame: LinearLayout
|
||||||
private var label: TextView
|
private var label: TextView
|
||||||
private var scoreRing: RingView
|
private var scoreRing: RingView
|
||||||
|
|
||||||
|
private var currentToggleTaskId = 0
|
||||||
|
private var queuedToggles = mutableListOf<DelayedToggle>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scoreRing = RingView(context).apply {
|
scoreRing = RingView(context).apply {
|
||||||
val thickness = dp(3f)
|
val thickness = dp(3f)
|
||||||
@@ -143,7 +159,14 @@ class HabitCardView(
|
|||||||
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
checkmarkPanel = checkmarkPanelFactory.create().apply {
|
||||||
onToggle = { timestamp, value ->
|
onToggle = { timestamp, value ->
|
||||||
triggerRipple(timestamp)
|
triggerRipple(timestamp)
|
||||||
habit?.let { behavior.onToggle(it, timestamp, value) }
|
habit?.let {
|
||||||
|
val taskId = queueToggle(it, timestamp, value);
|
||||||
|
{ runPendingToggles(taskId) }.delay(TOGGLE_DELAY_MILLIS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEdit = { timestamp ->
|
||||||
|
triggerRipple(timestamp)
|
||||||
|
habit?.let { behavior.onEdit(it, timestamp) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +202,24 @@ class HabitCardView(
|
|||||||
addView(innerFrame)
|
addView(innerFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun runPendingToggles(id: Int) {
|
||||||
|
if (currentToggleTaskId != id) return
|
||||||
|
for ((h, t, v) in queuedToggles) behavior.onToggle(h, t, v)
|
||||||
|
queuedToggles.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun queueToggle(
|
||||||
|
it: Habit,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
value: Int
|
||||||
|
): Int {
|
||||||
|
currentToggleTaskId += 1
|
||||||
|
queuedToggles.add(DelayedToggle(it, timestamp, value))
|
||||||
|
return currentToggleTaskId
|
||||||
|
}
|
||||||
|
|
||||||
override fun onModelChange() {
|
override fun onModelChange() {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
habit?.let { copyAttributesFrom(it) }
|
habit?.let { copyAttributesFrom(it) }
|
||||||
@@ -236,6 +277,7 @@ class HabitCardView(
|
|||||||
numberPanel.apply {
|
numberPanel.apply {
|
||||||
color = c
|
color = c
|
||||||
units = h.unit
|
units = h.unit
|
||||||
|
targetType = h.targetType
|
||||||
threshold = h.targetValue
|
threshold = h.targetValue
|
||||||
visibility = when (h.isNumerical) {
|
visibility = when (h.isNumerical) {
|
||||||
true -> View.VISIBLE
|
true -> View.VISIBLE
|
||||||
@@ -262,4 +304,12 @@ class HabitCardView(
|
|||||||
}
|
}
|
||||||
innerFrame.setBackgroundResource(background)
|
innerFrame.setBackgroundResource(background)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TOGGLE_DELAY_MILLIS = 2000L
|
||||||
|
|
||||||
|
fun (() -> Unit).delay(delayInMillis: Long) {
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed(this, delayInMillis)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,15 @@ import android.view.View
|
|||||||
import android.view.View.OnClickListener
|
import android.view.View.OnClickListener
|
||||||
import android.view.View.OnLongClickListener
|
import android.view.View.OnLongClickListener
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType.AT_LEAST
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType.AT_MOST
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.inject.ActivityContext
|
import org.isoron.uhabits.inject.ActivityContext
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils.getDimension
|
import org.isoron.uhabits.utils.InterfaceUtils.getDimension
|
||||||
import org.isoron.uhabits.utils.StyledResources
|
|
||||||
import org.isoron.uhabits.utils.dim
|
import org.isoron.uhabits.utils.dim
|
||||||
|
import org.isoron.uhabits.utils.drawNotesIndicator
|
||||||
import org.isoron.uhabits.utils.getFontAwesome
|
import org.isoron.uhabits.utils.getFontAwesome
|
||||||
import org.isoron.uhabits.utils.showMessage
|
import org.isoron.uhabits.utils.sres
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -88,11 +90,22 @@ class NumberButtonView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var targetType = AT_LEAST
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
var units = ""
|
var units = ""
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
var hasNotes = false
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
var onEdit: () -> Unit = {}
|
var onEdit: () -> Unit = {}
|
||||||
private var drawer: Drawer = Drawer(context)
|
private var drawer: Drawer = Drawer(context)
|
||||||
@@ -103,8 +116,7 @@ class NumberButtonView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
if (preferences.isShortToggleEnabled) onEdit()
|
onEdit()
|
||||||
else showMessage(resources.getString(R.string.long_press_to_edit))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLongClick(v: View): Boolean {
|
override fun onLongClick(v: View): Boolean {
|
||||||
@@ -127,7 +139,6 @@ class NumberButtonView(
|
|||||||
|
|
||||||
private val em: Float
|
private val em: Float
|
||||||
private val rect: RectF = RectF()
|
private val rect: RectF = RectF()
|
||||||
private val sr = StyledResources(context)
|
|
||||||
|
|
||||||
private val lowContrast: Int
|
private val lowContrast: Int
|
||||||
private val mediumContrast: Int
|
private val mediumContrast: Int
|
||||||
@@ -148,15 +159,16 @@ class NumberButtonView(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
em = pNumber.measureText("m")
|
em = pNumber.measureText("m")
|
||||||
lowContrast = sr.getColor(R.attr.contrast40)
|
lowContrast = sres.getColor(R.attr.contrast40)
|
||||||
mediumContrast = sr.getColor(R.attr.contrast60)
|
mediumContrast = sres.getColor(R.attr.contrast60)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun draw(canvas: Canvas) {
|
fun draw(canvas: Canvas) {
|
||||||
val activeColor = when {
|
val activeColor = when {
|
||||||
value <= 0.0 -> lowContrast
|
value < 0.0 -> lowContrast
|
||||||
value < threshold -> mediumContrast
|
(targetType == AT_LEAST) && (value >= threshold) -> color
|
||||||
else -> color
|
(targetType == AT_MOST) && (value <= threshold) -> color
|
||||||
|
else -> mediumContrast
|
||||||
}
|
}
|
||||||
|
|
||||||
val label: String
|
val label: String
|
||||||
@@ -196,6 +208,8 @@ class NumberButtonView(
|
|||||||
rect.offset(0f, 1.3f * em)
|
rect.offset(0f, 1.3f * em)
|
||||||
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
canvas.drawText(units, rect.centerX(), rect.centerY(), pUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawNotesIndicator(canvas, color, em, hasNotes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
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 org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.models.Timestamp
|
import org.isoron.uhabits.core.models.Timestamp
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.utils.DateUtils
|
import org.isoron.uhabits.core.utils.DateUtils
|
||||||
@@ -47,6 +48,12 @@ class NumberPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var targetType = NumericalHabitType.AT_LEAST
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
var threshold = 0.0
|
var threshold = 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
@@ -65,6 +72,12 @@ class NumberPanelView(
|
|||||||
setupButtons()
|
setupButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var notesIndicators = BooleanArray(0)
|
||||||
|
set(values) {
|
||||||
|
field = values
|
||||||
|
setupButtons()
|
||||||
|
}
|
||||||
|
|
||||||
var onEdit: (Timestamp) -> Unit = {}
|
var onEdit: (Timestamp) -> Unit = {}
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
@@ -83,7 +96,12 @@ class NumberPanelView(
|
|||||||
index + dataOffset < values.size -> values[index + dataOffset]
|
index + dataOffset < values.size -> values[index + dataOffset]
|
||||||
else -> 0.0
|
else -> 0.0
|
||||||
}
|
}
|
||||||
|
button.hasNotes = when {
|
||||||
|
index + dataOffset < notesIndicators.size -> notesIndicators[index + dataOffset]
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
button.color = color
|
button.color = color
|
||||||
|
button.targetType = targetType
|
||||||
button.threshold = threshold
|
button.threshold = threshold
|
||||||
button.units = units
|
button.units = units
|
||||||
button.onEdit = { onEdit(timestamp) }
|
button.onEdit = { onEdit(timestamp) }
|
||||||
|
|||||||
@@ -27,17 +27,20 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.isoron.platform.time.LocalDate
|
||||||
import org.isoron.uhabits.AndroidDirFinder
|
import org.isoron.uhabits.AndroidDirFinder
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.activities.HabitsDirFinder
|
import org.isoron.uhabits.activities.HabitsDirFinder
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.CheckmarkDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
import org.isoron.uhabits.activities.common.dialogs.HistoryEditorDialog
|
||||||
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
|
||||||
import org.isoron.uhabits.core.commands.Command
|
import org.isoron.uhabits.core.commands.Command
|
||||||
import org.isoron.uhabits.core.commands.CommandRunner
|
import org.isoron.uhabits.core.commands.CommandRunner
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
|
import org.isoron.uhabits.core.models.PaletteColor
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
|
||||||
@@ -164,9 +167,29 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener {
|
|||||||
override fun showNumberPicker(
|
override fun showNumberPicker(
|
||||||
value: Double,
|
value: Double,
|
||||||
unit: String,
|
unit: String,
|
||||||
|
notes: String,
|
||||||
|
dateString: String,
|
||||||
callback: ListHabitsBehavior.NumberPickerCallback,
|
callback: ListHabitsBehavior.NumberPickerCallback,
|
||||||
) {
|
) {
|
||||||
NumberPickerFactory(this@ShowHabitActivity).create(value, unit, callback).show()
|
NumberPickerFactory(this@ShowHabitActivity).create(value, unit, notes, dateString, callback).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showCheckmarkDialog(
|
||||||
|
selectedValue: Int,
|
||||||
|
notes: String,
|
||||||
|
date: LocalDate,
|
||||||
|
preferences: Preferences,
|
||||||
|
color: PaletteColor,
|
||||||
|
callback: ListHabitsBehavior.CheckMarkDialogCallback
|
||||||
|
) {
|
||||||
|
CheckmarkDialog(this@ShowHabitActivity, preferences).create(
|
||||||
|
selectedValue,
|
||||||
|
notes,
|
||||||
|
date,
|
||||||
|
color,
|
||||||
|
callback,
|
||||||
|
themeSwitcher.currentTheme!!,
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showEditHabitScreen(habit: Habit) {
|
override fun showEditHabitScreen(habit: Habit) {
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ class HistoryCardView(context: Context, attrs: AttributeSet) : LinearLayout(cont
|
|||||||
theme = state.theme,
|
theme = state.theme,
|
||||||
dateFormatter = JavaLocalDateFormatter(Locale.getDefault()),
|
dateFormatter = JavaLocalDateFormatter(Locale.getDefault()),
|
||||||
series = state.series,
|
series = state.series,
|
||||||
|
defaultSquare = state.defaultSquare,
|
||||||
|
notesIndicators = state.notesIndicators,
|
||||||
firstWeekday = state.firstWeekday,
|
firstWeekday = state.firstWeekday,
|
||||||
)
|
)
|
||||||
binding.chart.postInvalidate()
|
binding.chart.postInvalidate()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.isoron.platform.gui.toInt
|
|||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
import org.isoron.uhabits.activities.habits.edit.formatFrequency
|
import org.isoron.uhabits.activities.habits.edit.formatFrequency
|
||||||
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
import org.isoron.uhabits.activities.habits.list.views.toShortString
|
||||||
|
import org.isoron.uhabits.core.models.NumericalHabitType
|
||||||
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
|
||||||
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
import org.isoron.uhabits.databinding.ShowHabitSubtitleBinding
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils
|
import org.isoron.uhabits.utils.InterfaceUtils
|
||||||
@@ -65,7 +66,12 @@ class SubtitleCardView(context: Context, attrs: AttributeSet) : LinearLayout(con
|
|||||||
binding.questionLabel.visibility = View.VISIBLE
|
binding.questionLabel.visibility = View.VISIBLE
|
||||||
binding.targetIcon.visibility = View.VISIBLE
|
binding.targetIcon.visibility = View.VISIBLE
|
||||||
binding.targetText.visibility = View.VISIBLE
|
binding.targetText.visibility = View.VISIBLE
|
||||||
if (!state.isNumerical) {
|
if (state.isNumerical) {
|
||||||
|
binding.targetIcon.text = when (state.targetType) {
|
||||||
|
NumericalHabitType.AT_LEAST -> resources.getString(R.string.fa_arrow_circle_up)
|
||||||
|
else -> resources.getString(R.string.fa_arrow_circle_down)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
binding.targetIcon.visibility = View.GONE
|
binding.targetIcon.visibility = View.GONE
|
||||||
binding.targetText.visibility = View.GONE
|
binding.targetText.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ package org.isoron.uhabits.activities.intro
|
|||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.github.paolorotolo.appintro.AppIntro2
|
import androidx.fragment.app.Fragment
|
||||||
import com.github.paolorotolo.appintro.AppIntroFragment
|
import com.github.appintro.AppIntro2
|
||||||
|
import com.github.appintro.AppIntroFragment
|
||||||
import org.isoron.uhabits.R
|
import org.isoron.uhabits.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,9 @@ import org.isoron.uhabits.R
|
|||||||
* launched for the first time.
|
* launched for the first time.
|
||||||
*/
|
*/
|
||||||
class IntroActivity : AppIntro2() {
|
class IntroActivity : AppIntro2() {
|
||||||
override fun init(savedInstanceState: Bundle?) {
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
showStatusBar(false)
|
showStatusBar(false)
|
||||||
|
|
||||||
addSlide(
|
addSlide(
|
||||||
@@ -61,11 +64,13 @@ class IntroActivity : AppIntro2() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNextPressed() {}
|
override fun onDonePressed(currentFragment: Fragment?) {
|
||||||
|
super.onDonePressed(currentFragment)
|
||||||
override fun onDonePressed() {
|
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSlideChanged() {}
|
override fun onSkipPressed(currentFragment: Fragment?) {
|
||||||
|
super.onSkipPressed(currentFragment)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,17 +23,17 @@ import android.os.Bundle
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
import org.isoron.uhabits.activities.AndroidThemeSwitcher
|
||||||
import org.isoron.uhabits.core.models.HabitMatcherBuilder
|
import org.isoron.uhabits.core.models.HabitMatcher
|
||||||
|
|
||||||
class EditSettingActivity : AppCompatActivity() {
|
class EditSettingActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val app = applicationContext as HabitsApplication
|
val app = applicationContext as HabitsApplication
|
||||||
val habits = app.component.habitList.getFiltered(
|
val habits = app.component.habitList.getFiltered(
|
||||||
HabitMatcherBuilder()
|
HabitMatcher(
|
||||||
.setArchivedAllowed(false)
|
isArchivedAllowed = false,
|
||||||
.setCompletedAllowed(true)
|
isCompletedAllowed = true,
|
||||||
.build()
|
)
|
||||||
)
|
)
|
||||||
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
AndroidThemeSwitcher(this, app.component.preferences).apply()
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ import android.app.AlarmManager.RTC_WAKEUP
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.ALARM_SERVICE
|
import android.content.Context.ALARM_SERVICE
|
||||||
import android.os.Build.VERSION.SDK_INT
|
|
||||||
import android.os.Build.VERSION_CODES.M
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.isoron.uhabits.core.AppScope
|
import org.isoron.uhabits.core.AppScope
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -58,10 +56,7 @@ class IntentScheduler
|
|||||||
)
|
)
|
||||||
return SchedulerResult.IGNORED
|
return SchedulerResult.IGNORED
|
||||||
}
|
}
|
||||||
if (SDK_INT >= M)
|
manager.setExactAndAllowWhileIdle(alarmType, timestamp, intent)
|
||||||
manager.setExactAndAllowWhileIdle(alarmType, timestamp, intent)
|
|
||||||
else
|
|
||||||
manager.setExact(alarmType, timestamp, intent)
|
|
||||||
return SchedulerResult.OK
|
return SchedulerResult.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
package org.isoron.uhabits.intents
|
package org.isoron.uhabits.intents
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.PendingIntent.getBroadcast
|
import android.app.PendingIntent.getBroadcast
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -49,7 +50,7 @@ class PendingIntentFactory
|
|||||||
action = WidgetReceiver.ACTION_ADD_REPETITION
|
action = WidgetReceiver.ACTION_ADD_REPETITION
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun dismissNotification(habit: Habit): PendingIntent =
|
fun dismissNotification(habit: Habit): PendingIntent =
|
||||||
@@ -60,7 +61,7 @@ class PendingIntentFactory
|
|||||||
action = WidgetReceiver.ACTION_DISMISS_REMINDER
|
action = WidgetReceiver.ACTION_DISMISS_REMINDER
|
||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun removeRepetition(habit: Habit, timestamp: Timestamp?): PendingIntent =
|
fun removeRepetition(habit: Habit, timestamp: Timestamp?): PendingIntent =
|
||||||
@@ -72,7 +73,7 @@ class PendingIntentFactory
|
|||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
if (timestamp != null) putExtra("timestamp", timestamp.unixTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun showHabit(habit: Habit): PendingIntent =
|
fun showHabit(habit: Habit): PendingIntent =
|
||||||
@@ -84,7 +85,7 @@ class PendingIntentFactory
|
|||||||
habit
|
habit
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.getPendingIntent(0, FLAG_UPDATE_CURRENT)!!
|
.getPendingIntent(0, FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT)!!
|
||||||
|
|
||||||
fun showReminder(
|
fun showReminder(
|
||||||
habit: Habit,
|
habit: Habit,
|
||||||
@@ -100,7 +101,7 @@ class PendingIntentFactory
|
|||||||
putExtra("timestamp", timestamp)
|
putExtra("timestamp", timestamp)
|
||||||
putExtra("reminderTime", reminderTime)
|
putExtra("reminderTime", reminderTime)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun snoozeNotification(habit: Habit): PendingIntent =
|
fun snoozeNotification(habit: Habit): PendingIntent =
|
||||||
@@ -111,7 +112,7 @@ class PendingIntentFactory
|
|||||||
data = Uri.parse(habit.uriString)
|
data = Uri.parse(habit.uriString)
|
||||||
action = ReminderReceiver.ACTION_SNOOZE_REMINDER
|
action = ReminderReceiver.ACTION_SNOOZE_REMINDER
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun toggleCheckmark(habit: Habit, timestamp: Long?): PendingIntent =
|
fun toggleCheckmark(habit: Habit, timestamp: Long?): PendingIntent =
|
||||||
@@ -123,7 +124,7 @@ class PendingIntentFactory
|
|||||||
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
action = WidgetReceiver.ACTION_TOGGLE_REPETITION
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun setNumericalValue(
|
fun setNumericalValue(
|
||||||
@@ -142,7 +143,7 @@ class PendingIntentFactory
|
|||||||
putExtra("numericalValue", numericalValue)
|
putExtra("numericalValue", numericalValue)
|
||||||
if (timestamp != null) putExtra("timestamp", timestamp)
|
if (timestamp != null) putExtra("timestamp", timestamp)
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
fun updateWidgets(): PendingIntent =
|
fun updateWidgets(): PendingIntent =
|
||||||
@@ -152,6 +153,6 @@ class PendingIntentFactory
|
|||||||
Intent(context, WidgetReceiver::class.java).apply {
|
Intent(context, WidgetReceiver::class.java).apply {
|
||||||
action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE
|
action = WidgetReceiver.ACTION_UPDATE_WIDGETS_VALUE
|
||||||
},
|
},
|
||||||
FLAG_UPDATE_CURRENT
|
FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,13 +153,15 @@ class AndroidNotificationTray
|
|||||||
if (preferences.shouldMakeNotificationsLed())
|
if (preferences.shouldMakeNotificationsLed())
|
||||||
builder.setLights(Color.RED, 1000, 1000)
|
builder.setLights(Color.RED, 1000, 1000)
|
||||||
|
|
||||||
val snoozeAction = Action(
|
if (SDK_INT < Build.VERSION_CODES.S) {
|
||||||
R.drawable.ic_action_snooze,
|
val snoozeAction = Action(
|
||||||
context.getString(R.string.snooze),
|
R.drawable.ic_action_snooze,
|
||||||
pendingIntents.snoozeNotification(habit)
|
context.getString(R.string.snooze),
|
||||||
)
|
pendingIntents.snoozeNotification(habit)
|
||||||
wearableExtender.addAction(snoozeAction)
|
)
|
||||||
builder.addAction(snoozeAction)
|
wearableExtender.addAction(snoozeAction)
|
||||||
|
builder.addAction(snoozeAction)
|
||||||
|
}
|
||||||
|
|
||||||
builder.extend(wearableExtender)
|
builder.extend(wearableExtender)
|
||||||
return builder.build()
|
return builder.build()
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
@@ -76,8 +78,21 @@ class ReminderReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
ACTION_SNOOZE_REMINDER -> {
|
ACTION_SNOOZE_REMINDER -> {
|
||||||
if (habit == null) return
|
if (habit == null) return
|
||||||
Log.d("ReminderReceiver", String.format("onSnoozePressed habit=%d", habit.id))
|
if (SDK_INT < Build.VERSION_CODES.S) {
|
||||||
reminderController.onSnoozePressed(habit, context)
|
Log.d(
|
||||||
|
"ReminderReceiver",
|
||||||
|
String.format("onSnoozePressed habit=%d", habit.id)
|
||||||
|
)
|
||||||
|
reminderController.onSnoozePressed(habit, context)
|
||||||
|
} else {
|
||||||
|
Log.w(
|
||||||
|
"ReminderReceiver",
|
||||||
|
String.format(
|
||||||
|
"onSnoozePressed habit=%d, should be deactivated in recent versions.",
|
||||||
|
habit.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Intent.ACTION_BOOT_COMPLETED -> {
|
Intent.ACTION_BOOT_COMPLETED -> {
|
||||||
Log.d("ReminderReceiver", "onBootCompleted")
|
Log.d("ReminderReceiver", "onBootCompleted")
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ package org.isoron.uhabits.utils
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@@ -199,5 +201,15 @@ fun View.dim(id: Int) = InterfaceUtils.getDimension(context, id)
|
|||||||
fun View.sp(value: Float) = InterfaceUtils.spToPixels(context, value)
|
fun View.sp(value: Float) = InterfaceUtils.spToPixels(context, value)
|
||||||
fun View.dp(value: Float) = InterfaceUtils.dpToPixels(context, value)
|
fun View.dp(value: Float) = InterfaceUtils.dpToPixels(context, value)
|
||||||
fun View.str(id: Int) = resources.getString(id)
|
fun View.str(id: Int) = resources.getString(id)
|
||||||
|
|
||||||
|
fun View.drawNotesIndicator(canvas: Canvas, color: Int, size: Float, hasNotes: Boolean) {
|
||||||
|
val pNotesIndicator = Paint()
|
||||||
|
pNotesIndicator.color = color
|
||||||
|
if (hasNotes) {
|
||||||
|
val cy = 0.8f * size
|
||||||
|
canvas.drawCircle(width.toFloat() - cy, cy, 8f, pNotesIndicator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val View.sres: StyledResources
|
val View.sres: StyledResources
|
||||||
get() = StyledResources(context)
|
get() = StyledResources(context)
|
||||||
|
|||||||
@@ -56,7 +56,10 @@ class HistoryWidget(
|
|||||||
theme = WidgetTheme(),
|
theme = WidgetTheme(),
|
||||||
)
|
)
|
||||||
(widgetView.dataView as AndroidDataView).apply {
|
(widgetView.dataView as AndroidDataView).apply {
|
||||||
(this.view as HistoryChart).series = model.series
|
val historyChart = (this.view as HistoryChart)
|
||||||
|
historyChart.series = model.series
|
||||||
|
historyChart.defaultSquare = model.defaultSquare
|
||||||
|
historyChart.notesIndicators = model.notesIndicators
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +74,8 @@ class HistoryWidget(
|
|||||||
dateFormatter = JavaLocalDateFormatter(Locale.getDefault()),
|
dateFormatter = JavaLocalDateFormatter(Locale.getDefault()),
|
||||||
firstWeekday = prefs.firstWeekday,
|
firstWeekday = prefs.firstWeekday,
|
||||||
series = listOf(),
|
series = listOf(),
|
||||||
|
defaultSquare = HistoryChart.Square.OFF,
|
||||||
|
notesIndicators = listOf(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
).apply {
|
).apply {
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import android.content.Intent
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
|
import org.isoron.platform.utils.StringUtils
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.utils.StringUtils
|
|
||||||
|
|
||||||
class StackWidget(
|
class StackWidget(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ import android.util.Log
|
|||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import android.widget.RemoteViewsService
|
import android.widget.RemoteViewsService
|
||||||
import android.widget.RemoteViewsService.RemoteViewsFactory
|
import android.widget.RemoteViewsService.RemoteViewsFactory
|
||||||
|
import org.isoron.platform.utils.StringUtils.Companion.splitLongs
|
||||||
import org.isoron.uhabits.HabitsApplication
|
import org.isoron.uhabits.HabitsApplication
|
||||||
import org.isoron.uhabits.core.models.Habit
|
import org.isoron.uhabits.core.models.Habit
|
||||||
import org.isoron.uhabits.core.models.HabitNotFoundException
|
import org.isoron.uhabits.core.models.HabitNotFoundException
|
||||||
import org.isoron.uhabits.core.preferences.Preferences
|
import org.isoron.uhabits.core.preferences.Preferences
|
||||||
import org.isoron.uhabits.core.utils.StringUtils.Companion.splitLongs
|
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels
|
import org.isoron.uhabits.utils.InterfaceUtils.dpToPixels
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi
|
|||||||
SystemUtils.unlockScreen(this)
|
SystemUtils.unlockScreen(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNumberPicked(newValue: Double) {
|
override fun onNumberPicked(newValue: Double, notes: String) {
|
||||||
behavior.setValue(data.habit, data.timestamp, (newValue * 1000).toInt())
|
behavior.setValue(data.habit, data.timestamp, (newValue * 1000).toInt(), notes)
|
||||||
widgetUpdater.updateWidgets()
|
widgetUpdater.updateWidgets()
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,8 @@ class NumericalCheckmarkWidgetActivity : Activity(), ListHabitsBehavior.NumberPi
|
|||||||
numberPickerFactory.create(
|
numberPickerFactory.create(
|
||||||
entry.value / 1000.0,
|
entry.value / 1000.0,
|
||||||
data.habit.unit,
|
data.habit.unit,
|
||||||
|
entry.notes,
|
||||||
|
today.toDialogDateString(),
|
||||||
this
|
this
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:alpha="0.8">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333"
|
|
||||||
android:alpha="0.6">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M20,12l-1.41,-1.41L13,16.17V4h-2v12.17l-5.58,-5.59L4,12l8,8 8,-8z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#FFFFFF"
|
|
||||||
android:alpha="0.8">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="#333333"
|
|
||||||
android:alpha="0.6">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M4,12l1.41,1.41L11,7.83V20h2V7.83l5.58,5.59L20,12l-8,-8 -8,8z"/>
|
|
||||||
</vector>
|
|
||||||
|
Before Width: | Height: | Size: 155 B |
|
Before Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 266 B |
|
Before Width: | Height: | Size: 432 B |
|
Before Width: | Height: | Size: 492 B |
|
Before Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 249 B |
|
Before Width: | Height: | Size: 304 B |
|
Before Width: | Height: | Size: 354 B |
|
Before Width: | Height: | Size: 111 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 237 B |
|
Before Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 246 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 618 B |
|
Before Width: | Height: | Size: 111 B |
|
Before Width: | Height: | Size: 214 B |
|
Before Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 289 B |
|
Before Width: | Height: | Size: 319 B |
|
Before Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 183 B |
|
Before Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 90 B |
|
Before Width: | Height: | Size: 694 B |
|
Before Width: | Height: | Size: 207 B |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 177 B |
|
Before Width: | Height: | Size: 182 B |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 435 B |
|
Before Width: | Height: | Size: 140 B |
|
Before Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 323 B |
|
Before Width: | Height: | Size: 488 B |
|
Before Width: | Height: | Size: 541 B |
|
Before Width: | Height: | Size: 602 B |
|
Before Width: | Height: | Size: 279 B |
|
Before Width: | Height: | Size: 267 B |
|
Before Width: | Height: | Size: 296 B |
|
Before Width: | Height: | Size: 103 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 317 B |