mirror of https://github.com/tasks/tasks
Compare commits
320 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
fbfcbdc555 | 2 weeks ago |
|
|
51e347f22b | 2 weeks ago |
|
|
d0c28baf7b | 2 weeks ago |
|
|
3d4d44849e | 3 weeks ago |
|
|
dfa41c515a | 3 weeks ago |
|
|
a539b3a3e4 | 3 weeks ago |
|
|
8d6de19b2a | 3 weeks ago |
|
|
2a6e1638c9 | 3 weeks ago |
|
|
9190930745 | 3 weeks ago |
|
|
40961dad87 | 3 weeks ago |
|
|
9fbe27345d | 4 weeks ago |
|
|
5f67e0ea3a | 4 weeks ago |
|
|
d51171b17e | 4 weeks ago |
|
|
b657773a2d | 4 weeks ago |
|
|
d84effc447 | 4 weeks ago |
|
|
21db540614 | 4 weeks ago |
|
|
8e9f27c46e | 4 weeks ago |
|
|
94ad2a381e | 4 weeks ago |
|
|
747928c8c7 | 4 weeks ago |
|
|
9a28f1062b | 4 weeks ago |
|
|
ffe749bf0c | 4 weeks ago |
|
|
b0ae0129ae | 4 weeks ago |
|
|
6715369d93 | 4 weeks ago |
|
|
4efb678699 | 4 weeks ago |
|
|
581b789a0b | 4 weeks ago |
|
|
15d8b3aa59 | 4 weeks ago |
|
|
dcd5d8c094 | 4 weeks ago |
|
|
f3253e6188 | 4 weeks ago |
|
|
34b0c62ef8 | 4 weeks ago |
|
|
3754196714 | 4 weeks ago |
|
|
b94a91efbe | 4 weeks ago |
|
|
2c6066c378 | 4 weeks ago |
|
|
03e15a8c35 | 4 weeks ago |
|
|
e63add73bc | 4 weeks ago |
|
|
bf676bcea7 | 4 weeks ago |
|
|
6de8fe2fa0 | 4 weeks ago |
|
|
5907f27172 | 4 weeks ago |
|
|
38119d4560 | 4 weeks ago |
|
|
52848a5308 | 4 weeks ago |
|
|
4c492120b3 | 4 weeks ago |
|
|
20e995b19b | 4 weeks ago |
|
|
2b63e33de2 | 4 weeks ago |
|
|
5980bd497d | 2 months ago |
|
|
e16b5cd6cd | 2 months ago |
|
|
3d9945c798 | 2 months ago |
|
|
5c9eb1c35f | 2 months ago |
|
|
6b594a3213 | 2 months ago |
|
|
9e0e01f89b | 2 months ago |
|
|
932b8b0540 | 3 months ago |
|
|
152a9684e5 | 3 months ago |
|
|
88c817b770 | 3 months ago |
|
|
a368960073 | 3 months ago |
|
|
4f1cc5ab8e | 3 months ago |
|
|
17f54b6d32 | 3 months ago |
|
|
6a44bed0e8 | 3 months ago |
|
|
7fa90396b3 | 3 months ago |
|
|
d1df39d12c | 3 months ago |
|
|
d9ddd45f13 | 3 months ago |
|
|
27b21118eb | 3 months ago |
|
|
f725365f87 | 3 months ago |
|
|
7377e4672d | 3 months ago |
|
|
8c90b1ec87 | 3 months ago |
|
|
5b50f45a5b | 3 months ago |
|
|
882338f554 | 3 months ago |
|
|
930e980550 | 3 months ago |
|
|
fb243c7aaf | 3 months ago |
|
|
c3842fd2f7 | 3 months ago |
|
|
81ecb322e9 | 3 months ago |
|
|
1b386458b8 | 3 months ago |
|
|
7dca092831 | 3 months ago |
|
|
2749b029c5 | 3 months ago |
|
|
0cb19221c4 | 3 months ago |
|
|
d900f72a5c | 3 months ago |
|
|
0d8979b72c | 3 months ago |
|
|
703322f510 | 3 months ago |
|
|
192351a4b8 | 3 months ago |
|
|
844a3a0ff8 | 3 months ago |
|
|
e6bbc8d361 | 3 months ago |
|
|
04ab41f622 | 3 months ago |
|
|
4946b0ca06 | 3 months ago |
|
|
2c29194ff2 | 3 months ago |
|
|
1aaaad86da | 3 months ago |
|
|
68601873fd | 3 months ago |
|
|
c9721790ce | 3 months ago |
|
|
6f16a29fd7 | 3 months ago |
|
|
a501b81bfc | 3 months ago |
|
|
fded7fbdd5 | 3 months ago |
|
|
3ff4a2339b | 3 months ago |
|
|
e400594e5b | 3 months ago |
|
|
8bbbc1dcac | 3 months ago |
|
|
38d27b262a | 3 months ago |
|
|
b61842646b | 3 months ago |
|
|
29cbb33a42 | 3 months ago |
|
|
2418b664e9 | 3 months ago |
|
|
0e3803c28d | 3 months ago |
|
|
c0bb7b306a | 3 months ago |
|
|
903412fdea | 3 months ago |
|
|
12b1127e6b | 3 months ago |
|
|
c3ce7a43fb | 3 months ago |
|
|
a391dff9bc | 3 months ago |
|
|
28e12110fa | 3 months ago |
|
|
0bba2c4a63 | 3 months ago |
|
|
700421c5ce | 3 months ago |
|
|
d4a742b136 | 3 months ago |
|
|
585967c601 | 3 months ago |
|
|
d279f7c42e | 3 months ago |
|
|
5cfbe9c8cb | 3 months ago |
|
|
f591b1846c | 3 months ago |
|
|
3401a59716 | 3 months ago |
|
|
d0328b378a | 3 months ago |
|
|
f9d859a33e | 3 months ago |
|
|
5a1560e513 | 3 months ago |
|
|
655cdc1a9d | 3 months ago |
|
|
f2235e6aa6 | 3 months ago |
|
|
09baafb47f | 3 months ago |
|
|
2d0cfaa04d | 3 months ago |
|
|
e60d516fcf | 3 months ago |
|
|
9ba82c3a01 | 3 months ago |
|
|
b7abdfe2ea | 3 months ago |
|
|
3da6f67ace | 4 months ago |
|
|
eec3ae447a | 4 months ago |
|
|
7a9a27eae0 | 4 months ago |
|
|
05b5f1470a | 4 months ago |
|
|
2a94af70fd | 4 months ago |
|
|
464903bf4d | 4 months ago |
|
|
0542f24c29 | 4 months ago |
|
|
0fe834b46c | 4 months ago |
|
|
997810af4c | 4 months ago |
|
|
6829f3f690 | 4 months ago |
|
|
cec5c1e4b8 | 4 months ago |
|
|
d3a12b039a | 4 months ago |
|
|
67dcc1db38 | 4 months ago |
|
|
83ae176288 | 4 months ago |
|
|
5e535b6d46 | 4 months ago |
|
|
5c124047e8 | 4 months ago |
|
|
8a332c8b2a | 4 months ago |
|
|
9d6a925fca | 4 months ago |
|
|
8cc28aa88b | 4 months ago |
|
|
99ea6cb0eb | 4 months ago |
|
|
a698236f4d | 4 months ago |
|
|
ad616472b3 | 4 months ago |
|
|
d5cda9e84b | 4 months ago |
|
|
9808ca1745 | 4 months ago |
|
|
0b76caa9a8 | 4 months ago |
|
|
07ac9f9ead | 4 months ago |
|
|
977edf4d8d | 4 months ago |
|
|
071d670c6d | 4 months ago |
|
|
eb89cc689a | 4 months ago |
|
|
a5c73ccc24 | 4 months ago |
|
|
51884d46f2 | 4 months ago |
|
|
101d7f2357 | 4 months ago |
|
|
75563b6a61 | 4 months ago |
|
|
3028d492b2 | 4 months ago |
|
|
ab2fc34e98 | 4 months ago |
|
|
852ac708b5 | 4 months ago |
|
|
092f357719 | 4 months ago |
|
|
ad1ace8fbf | 4 months ago |
|
|
204f49fc25 | 4 months ago |
|
|
9ef95291c8 | 4 months ago |
|
|
3e034ab91f | 4 months ago |
|
|
6f89ac3b93 | 4 months ago |
|
|
7d2ebf9cdf | 4 months ago |
|
|
16011b1963 | 4 months ago |
|
|
2f6348c53d | 4 months ago |
|
|
566c22c17e | 4 months ago |
|
|
2c33be700a | 4 months ago |
|
|
7a24f43387 | 4 months ago |
|
|
370ac149d3 | 4 months ago |
|
|
4c851ce7f3 | 4 months ago |
|
|
7c78854663 | 4 months ago |
|
|
d05730399d | 4 months ago |
|
|
c8f564d2d5 | 4 months ago |
|
|
65db4ab926 | 4 months ago |
|
|
1476e7fb27 | 4 months ago |
|
|
2ee0939564 | 4 months ago |
|
|
4c530a5de3 | 4 months ago |
|
|
fcd62c6801 | 4 months ago |
|
|
aedd29982a | 4 months ago |
|
|
3a37d6481e | 4 months ago |
|
|
c5f8583146 | 4 months ago |
|
|
9d96bed5b3 | 4 months ago |
|
|
2f268c8c70 | 4 months ago |
|
|
130a29d7e3 | 4 months ago |
|
|
dcb69394be | 4 months ago |
|
|
2cf3438e07 | 4 months ago |
|
|
06e9da41d6 | 4 months ago |
|
|
7b34e33c0e | 4 months ago |
|
|
be51651779 | 4 months ago |
|
|
627b05a575 | 4 months ago |
|
|
8207f30c5f | 5 months ago |
|
|
6811677d21 | 5 months ago |
|
|
9d88c5b3a0 | 5 months ago |
|
|
1f24a371fb | 5 months ago |
|
|
877a2cd6a5 | 5 months ago |
|
|
299b5b4d21 | 5 months ago |
|
|
e6320d42a7 | 5 months ago |
|
|
84c36a1a90 | 5 months ago |
|
|
73c0e38991 | 5 months ago |
|
|
773e822f14 | 5 months ago |
|
|
6f167b5ae0 | 5 months ago |
|
|
ba0cd26abc | 5 months ago |
|
|
3450db4006 | 5 months ago |
|
|
7c2cf38788 | 5 months ago |
|
|
e576a48eba | 5 months ago |
|
|
803593a3a7 | 5 months ago |
|
|
c4fc7fbadb | 5 months ago |
|
|
75d53fb8ac | 5 months ago |
|
|
769802c10a | 5 months ago |
|
|
14ff0086fa | 5 months ago |
|
|
761d4afeef | 5 months ago |
|
|
c7336589cd | 5 months ago |
|
|
e30c583d5a | 5 months ago |
|
|
38527aef0a | 5 months ago |
|
|
c9cdc4d50f | 5 months ago |
|
|
80753f607c | 5 months ago |
|
|
32cb067ffd | 5 months ago |
|
|
e93d0735d4 | 5 months ago |
|
|
103e7eaa60 | 5 months ago |
|
|
a490307251 | 5 months ago |
|
|
976df68671 | 5 months ago |
|
|
384f6e4604 | 5 months ago |
|
|
b68439b0e7 | 5 months ago |
|
|
3611593307 | 5 months ago |
|
|
c7a7384cf5 | 5 months ago |
|
|
f89789dd10 | 5 months ago |
|
|
91da4bc661 | 5 months ago |
|
|
30abeba683 | 5 months ago |
|
|
8f567a153a | 5 months ago |
|
|
2fbffa20cc | 5 months ago |
|
|
a071b05a71 | 5 months ago |
|
|
63dbb48d96 | 5 months ago |
|
|
e2c65c06a1 | 5 months ago |
|
|
03b1d78feb | 5 months ago |
|
|
fe72301c55 | 5 months ago |
|
|
ee40b72b02 | 5 months ago |
|
|
6181351db7 | 5 months ago |
|
|
8263ab2935 | 5 months ago |
|
|
38dbbe379b | 5 months ago |
|
|
58d5eea978 | 5 months ago |
|
|
f902ff38b0 | 5 months ago |
|
|
7c970eec95 | 5 months ago |
|
|
9ec448aedd | 5 months ago |
|
|
2fb1bb4873 | 5 months ago |
|
|
c64e581fd4 | 5 months ago |
|
|
36ec47e9bd | 5 months ago |
|
|
2942554ec8 | 5 months ago |
|
|
b6b624ce5b | 5 months ago |
|
|
1267c803c6 | 5 months ago |
|
|
7a17943142 | 5 months ago |
|
|
d73b8496cb | 5 months ago |
|
|
eda6eeaf62 | 5 months ago |
|
|
6d89f2cb02 | 5 months ago |
|
|
273fbf9153 | 5 months ago |
|
|
2b1ad31f76 | 5 months ago |
|
|
fdd1fc4989 | 5 months ago |
|
|
ff6a8ae0f1 | 5 months ago |
|
|
d1da6dc970 | 5 months ago |
|
|
a3dac4a397 | 5 months ago |
|
|
30060d8faf | 5 months ago |
|
|
a299363fe8 | 5 months ago |
|
|
36b20f47fd | 6 months ago |
|
|
5b1aff00df | 6 months ago |
|
|
f53aec3e8c | 6 months ago |
|
|
079d7867f1 | 6 months ago |
|
|
8b99e8feb2 | 6 months ago |
|
|
1a9b371bda | 6 months ago |
|
|
dfc311ff31 | 6 months ago |
|
|
cbcb812150 | 6 months ago |
|
|
70793f2433 | 6 months ago |
|
|
a198846902 | 6 months ago |
|
|
86a3b2b426 | 6 months ago |
|
|
bbac4da7d0 | 6 months ago |
|
|
f4e0d519d7 | 6 months ago |
|
|
9a584c851b | 6 months ago |
|
|
704edaa0ab | 6 months ago |
|
|
71833adf21 | 6 months ago |
|
|
4aad9bf00e | 6 months ago |
|
|
d4b1a0dd09 | 6 months ago |
|
|
0f1508f59a | 6 months ago |
|
|
266fe1281e | 6 months ago |
|
|
7e99762814 | 6 months ago |
|
|
56ec24d2f9 | 6 months ago |
|
|
fad3ab6ce3 | 6 months ago |
|
|
522bd6e304 | 6 months ago |
|
|
119572971d | 6 months ago |
|
|
48270d6f2c | 6 months ago |
|
|
44d15556c6 | 6 months ago |
|
|
33611d12bd | 6 months ago |
|
|
caa5916ad7 | 6 months ago |
|
|
821e8e0ee4 | 6 months ago |
|
|
fe4bd73d62 | 6 months ago |
|
|
b0493fdd7d | 6 months ago |
|
|
f330daa764 | 6 months ago |
|
|
017dd17021 | 6 months ago |
|
|
aa535981c3 | 6 months ago |
|
|
6f39614b5b | 6 months ago |
|
|
d33d87b6cb | 6 months ago |
|
|
a828d510e3 | 6 months ago |
|
|
898f84e3c8 | 6 months ago |
|
|
a1711fa0ea | 6 months ago |
|
|
542ba69870 | 6 months ago |
|
|
01d07cccbd | 6 months ago |
|
|
3e8838bdb6 | 6 months ago |
|
|
ac4c610841 | 6 months ago |
|
|
6bea199a75 | 6 months ago |
|
|
1efb8c8ee0 | 6 months ago |
|
|
d2c23a79de | 6 months ago |
|
|
0091f80945 | 6 months ago |
|
|
5cb770e722 | 6 months ago |
|
|
81759305c5 | 6 months ago |
|
|
1692a98d3d | 6 months ago |
|
|
9174855f2f | 6 months ago |
|
|
9632ea61d2 | 6 months ago |
|
|
e2ca2a2251 | 6 months ago |
|
|
dad35aafd3 | 6 months ago |
|
|
0735cb5e1d | 6 months ago |
|
|
55c8ab6e3a | 6 months ago |
|
|
5c4b345695 | 6 months ago |
|
|
2c6b1644dc | 6 months ago |
|
|
efaa2cf472 | 6 months ago |
@ -1 +1 @@
|
||||
3.3.6
|
||||
3.4.7
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
gem "abbrev"
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package org.tasks.data
|
||||
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.tasks.data.dao.CaldavDao
|
||||
import org.tasks.data.entity.CaldavAccount
|
||||
import org.tasks.injection.InjectingTestCase
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidTest
|
||||
class CaldavDaoExtensionsTest : InjectingTestCase() {
|
||||
@Inject lateinit var caldavDao: CaldavDao
|
||||
|
||||
@Test
|
||||
fun getLocalListCreatesAccountIfNeeded() = runBlocking {
|
||||
withTimeout(5000L) {
|
||||
assertTrue(caldavDao.getAccounts().isEmpty())
|
||||
caldavDao.getLocalList()
|
||||
assertTrue(caldavDao.getAccounts(CaldavAccount.TYPE_LOCAL).isNotEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package org.tasks.caldav.property
|
||||
|
||||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.PropertyFactory
|
||||
import at.bitfire.dav4jvm.XmlUtils
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import timber.log.Timber
|
||||
|
||||
data class CalendarIcon(
|
||||
val icon: String,
|
||||
): Property {
|
||||
companion object Companion {
|
||||
@JvmField
|
||||
val NAME = Property.Name(PropertyUtils.NS_TASKS, "x-calendar-icon")
|
||||
}
|
||||
|
||||
object Factory: PropertyFactory {
|
||||
|
||||
override fun getName() = NAME
|
||||
|
||||
override fun create(parser: XmlPullParser): CalendarIcon? {
|
||||
XmlUtils.readText(parser)?.takeIf { it.isNotBlank() }?.let {
|
||||
try {
|
||||
return CalendarIcon(it)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.e(e, "Couldn't parse icon: $it")
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,6 @@
|
||||
package org.tasks.caldav.property
|
||||
|
||||
import at.bitfire.dav4jvm.PropertyFactory
|
||||
import at.bitfire.dav4jvm.PropertyRegistry
|
||||
|
||||
object PropertyUtils {
|
||||
const val NS_TASKS = "http://org.tasks/ns/"
|
||||
const val NS_OWNCLOUD = "http://owncloud.org/ns"
|
||||
|
||||
fun PropertyRegistry.register(vararg factories: PropertyFactory) = register(factories.toList())
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package org.tasks.compose.home
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@Composable
|
||||
fun SystemBarScrim(
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(color.copy(alpha = 0.8f))
|
||||
.then(modifier)
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package org.tasks.compose.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Lock
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.tasks.themes.ThemeColor
|
||||
|
||||
@Composable
|
||||
fun ColorPicker(
|
||||
hasPro: Boolean,
|
||||
colors: List<ThemeColor>,
|
||||
onSelected: (ThemeColor) -> Unit,
|
||||
onColorWheelSelected: () -> Unit = {},
|
||||
) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 48.dp),
|
||||
contentPadding = PaddingValues(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
item {
|
||||
ColorWheelCircle(
|
||||
onClick = onColorWheelSelected,
|
||||
hasPro = hasPro,
|
||||
)
|
||||
}
|
||||
items(colors) { color ->
|
||||
ColorCircle(
|
||||
color = color,
|
||||
locked = !(hasPro || color.isFree),
|
||||
onClick = { onSelected(color) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorCircle(
|
||||
color: ThemeColor,
|
||||
locked: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.aspectRatio(1f)
|
||||
.clickable(onClick = onClick)
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color(color.primaryColor))
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (locked) {
|
||||
LockIcon(tint = Color(color.colorOnPrimary))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorWheelCircle(
|
||||
onClick: () -> Unit,
|
||||
hasPro: Boolean,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.aspectRatio(1f)
|
||||
.clickable(onClick = onClick)
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
brush = Brush.sweepGradient(
|
||||
colors = listOf(
|
||||
Color.Red,
|
||||
Color.Magenta,
|
||||
Color.Blue,
|
||||
Color.Cyan,
|
||||
Color.Green,
|
||||
Color.Yellow,
|
||||
Color.Red
|
||||
)
|
||||
)
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (!hasPro) {
|
||||
LockIcon(tint = Color.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LockIcon(tint: Color) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Lock,
|
||||
contentDescription = null,
|
||||
tint = tint,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package org.tasks.compose.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import org.tasks.themes.ThemeColor
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ColorPickerDialog(
|
||||
hasPro: Boolean,
|
||||
colors: List<ThemeColor>,
|
||||
onDismiss: () -> Unit,
|
||||
onColorSelected: (ThemeColor) -> Unit,
|
||||
onColorWheelSelected: () -> Unit = {},
|
||||
) {
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false)
|
||||
) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.9f)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Box(modifier = Modifier.padding(16.dp)) {
|
||||
ColorPicker(
|
||||
colors = colors,
|
||||
onSelected = { color ->
|
||||
onColorSelected(color)
|
||||
onDismiss()
|
||||
},
|
||||
onColorWheelSelected = {
|
||||
onColorWheelSelected()
|
||||
onDismiss()
|
||||
},
|
||||
hasPro = hasPro,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package org.tasks.jobs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import org.tasks.analytics.Firebase
|
||||
import org.tasks.caldav.CaldavClientProvider
|
||||
import org.tasks.caldav.property.CalendarIcon
|
||||
import org.tasks.data.dao.CaldavDao
|
||||
import org.tasks.data.entity.CaldavAccount
|
||||
import org.tasks.injection.BaseWorker
|
||||
import timber.log.Timber
|
||||
|
||||
@HiltWorker
|
||||
class UpgradeIconSyncWork @AssistedInject constructor(
|
||||
@Assisted context: Context,
|
||||
@Assisted workerParams: WorkerParameters,
|
||||
firebase: Firebase,
|
||||
private val clientProvider: CaldavClientProvider,
|
||||
private val caldavDao: CaldavDao,
|
||||
) : BaseWorker(context, workerParams, firebase) {
|
||||
override suspend fun run(): Result {
|
||||
var response = Result.success()
|
||||
caldavDao
|
||||
.getAccounts(CaldavAccount.TYPE_TASKS, CaldavAccount.TYPE_CALDAV)
|
||||
.forEach { account ->
|
||||
Timber.d("Uploading icons for $account")
|
||||
val caldavClient = clientProvider.forAccount(account)
|
||||
caldavClient.calendars().forEach { remote ->
|
||||
val url = remote.href
|
||||
val calendar = caldavDao
|
||||
.getCalendarByUrl(account.uuid!!, url.toString())
|
||||
?.takeIf { !it.readOnly() && it.icon?.isNotBlank() == true }
|
||||
?: run {
|
||||
Timber.d("No icon set for $url")
|
||||
return@forEach
|
||||
}
|
||||
val icon = remote[CalendarIcon::class.java]?.icon
|
||||
if (icon?.isNotBlank() == true) {
|
||||
Timber.d("Remote icon already set for $url")
|
||||
return@forEach
|
||||
}
|
||||
Timber.d("Uploading icon to ${calendar.icon} for $url")
|
||||
caldavClient.updateIcon(
|
||||
url = url,
|
||||
icon = calendar.icon,
|
||||
onFailure = { response = Result.retry() }
|
||||
)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
package org.tasks.preferences;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Build;
|
||||
import android.speech.RecognizerIntent;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.tasks.BuildConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Device {
|
||||
|
||||
private final Context context;
|
||||
|
||||
@Inject
|
||||
public Device(@ApplicationContext Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public boolean hasCamera() {
|
||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||
}
|
||||
|
||||
public boolean hasMicrophone() {
|
||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
|
||||
}
|
||||
|
||||
public boolean voiceInputAvailable() {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> activities =
|
||||
pm.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
|
||||
return (activities.size() != 0);
|
||||
}
|
||||
|
||||
public String getDebugInfo() {
|
||||
try {
|
||||
return Joiner.on("\n")
|
||||
.join(
|
||||
asList(
|
||||
"",
|
||||
"----------",
|
||||
"Tasks: "
|
||||
+ BuildConfig.VERSION_NAME
|
||||
+ " ("
|
||||
+ BuildConfig.FLAVOR
|
||||
+ " build "
|
||||
+ BuildConfig.VERSION_CODE
|
||||
+ ")",
|
||||
"Android: " + Build.VERSION.RELEASE + " (" + Build.DISPLAY + ")",
|
||||
"Locale: " + java.util.Locale.getDefault(),
|
||||
"Model: " + Build.MANUFACTURER + " " + Build.MODEL,
|
||||
"Product: " + Build.PRODUCT + " (" + Build.DEVICE + ")",
|
||||
"Kernel: "
|
||||
+ System.getProperty("os.version")
|
||||
+ " ("
|
||||
+ Build.VERSION.INCREMENTAL
|
||||
+ ")",
|
||||
"----------",
|
||||
""));
|
||||
} catch (Exception e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package org.tasks.preferences
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.speech.RecognizerIntent
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import org.tasks.BuildConfig
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class Device @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val permissionChecker: PermissionChecker,
|
||||
) {
|
||||
@SuppressLint("UnsupportedChromeOsCameraSystemFeature")
|
||||
fun hasCamera() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|
||||
|
||||
fun hasMicrophone() = context.packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
|
||||
|
||||
fun voiceInputAvailable(): Boolean {
|
||||
val pm = context.packageManager
|
||||
val activities =
|
||||
pm.queryIntentActivities(Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0)
|
||||
return activities.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun isDontKeepActivitiesEnabled(): Boolean? {
|
||||
return try {
|
||||
Settings.Global.getInt(context.contentResolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES) == 1
|
||||
} catch (e: Exception) {
|
||||
Timber.e("failed to fetch ${Settings.Global.ALWAYS_FINISH_ACTIVITIES}: ${e.message}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val debugInfo: String
|
||||
get() = """
|
||||
----------
|
||||
Tasks: ${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR} build ${BuildConfig.VERSION_CODE})
|
||||
Android: ${Build.VERSION.RELEASE} (${Build.DISPLAY})
|
||||
Locale: ${Locale.getDefault()}
|
||||
Model: ${Build.MANUFACTURER} ${Build.MODEL}
|
||||
Product: ${Build.PRODUCT} (${Build.DEVICE})
|
||||
Kernel: ${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})
|
||||
----------
|
||||
notifications: ${permissionChecker.hasNotificationPermission()}
|
||||
reminders: ${permissionChecker.hasAlarmsAndRemindersPermission()}
|
||||
background location: ${permissionChecker.canAccessBackgroundLocation()}
|
||||
foreground location: ${permissionChecker.canAccessForegroundLocation()}
|
||||
calendar: ${permissionChecker.canAccessCalendars()}
|
||||
----------
|
||||
dont keep activities: ${isDontKeepActivitiesEnabled()}
|
||||
----------
|
||||
""".trimIndent()
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package org.tasks.reminders;
|
||||
|
||||
public class Random {
|
||||
|
||||
private static final java.util.Random random = new java.util.Random();
|
||||
|
||||
public float nextFloat() {
|
||||
return random.nextFloat();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package org.tasks.reminders
|
||||
|
||||
import java.util.Random
|
||||
|
||||
open class Random {
|
||||
open fun nextFloat(seed: Long): Float {
|
||||
random.setSeed(seed)
|
||||
return random.nextFloat()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val random = Random()
|
||||
}
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
package org.tasks.security
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import android.util.Base64
|
||||
import org.tasks.Strings.isNullOrEmpty
|
||||
import timber.log.Timber
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.KeyStore
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import javax.crypto.*
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class KeyStoreEncryption @Inject constructor() {
|
||||
|
||||
private val keyStore: KeyStore = KeyStore.getInstance(ANDROID_KEYSTORE)
|
||||
|
||||
fun encrypt(text: String): String? {
|
||||
val iv = ByteArray(GCM_IV_LENGTH)
|
||||
SecureRandom().nextBytes(iv)
|
||||
val cipher = getCipher(Cipher.ENCRYPT_MODE, iv)
|
||||
return try {
|
||||
val output = cipher.doFinal(text.toByteArray(ENCODING))
|
||||
val result = ByteArray(iv.size + output.size)
|
||||
System.arraycopy(iv, 0, result, 0, iv.size)
|
||||
System.arraycopy(output, 0, result, iv.size, output.size)
|
||||
Base64.encodeToString(result, Base64.DEFAULT)
|
||||
} catch (e: IllegalBlockSizeException) {
|
||||
Timber.e(e)
|
||||
null
|
||||
} catch (e: BadPaddingException) {
|
||||
Timber.e(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun decrypt(text: String?): String? {
|
||||
if (isNullOrEmpty(text)) {
|
||||
return null
|
||||
}
|
||||
val decoded = Base64.decode(text, Base64.DEFAULT)
|
||||
val iv = Arrays.copyOfRange(decoded, 0, GCM_IV_LENGTH)
|
||||
val cipher = getCipher(Cipher.DECRYPT_MODE, iv)
|
||||
return try {
|
||||
val decrypted = cipher.doFinal(decoded, GCM_IV_LENGTH, decoded.size - GCM_IV_LENGTH)
|
||||
String(decrypted, ENCODING)
|
||||
} catch (e: IllegalBlockSizeException) {
|
||||
Timber.e(e)
|
||||
""
|
||||
} catch (e: BadPaddingException) {
|
||||
Timber.e(e)
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCipher(cipherMode: Int, iv: ByteArray): Cipher {
|
||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||
cipher.init(cipherMode, secretKey, GCMParameterSpec(GCM_TAG_LENGTH * java.lang.Byte.SIZE, iv))
|
||||
return cipher
|
||||
}
|
||||
|
||||
private val secretKey: SecretKey
|
||||
get() {
|
||||
val entry: KeyStore.Entry? = keyStore.getEntry(ALIAS, null)
|
||||
return (entry as KeyStore.SecretKeyEntry?)?.secretKey ?: generateNewKey()
|
||||
}
|
||||
|
||||
@SuppressLint("TrulyRandom")
|
||||
private fun generateNewKey(): SecretKey {
|
||||
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEYSTORE)
|
||||
keyGenerator.init(
|
||||
KeyGenParameterSpec.Builder(
|
||||
ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||
.setRandomizedEncryptionRequired(false)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||
.build())
|
||||
return keyGenerator.generateKey()
|
||||
}
|
||||
|
||||
init {
|
||||
keyStore.load(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
||||
private const val ALIAS = "passwords"
|
||||
private val ENCODING = StandardCharsets.UTF_8
|
||||
private const val GCM_IV_LENGTH = 12
|
||||
private const val GCM_TAG_LENGTH = 16
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue