Skip to content

Commit

Permalink
Merge branch 'release/1.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
sgrimault committed Jul 29, 2021
2 parents a35ea2c + 1b5c5c1 commit 7487203
Show file tree
Hide file tree
Showing 98 changed files with 3,259 additions and 1,734 deletions.
2 changes: 0 additions & 2 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.5.10'
ext.kotlin_version = '1.5.21'

repositories {
google()
Expand All @@ -12,7 +12,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jlleitschuh.gradle:ktlint-gradle:9.4.1"
// NOTE: Do not place any application dependencies here; they belong
Expand Down
4 changes: 2 additions & 2 deletions commons/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

version = "0.8.4"
version = "0.8.8"

android {
compileSdkVersion 29
Expand Down Expand Up @@ -54,7 +54,7 @@ dependencies {
api 'androidx.room:room-runtime:2.3.0'

testImplementation 'androidx.arch.core:core-testing:2.1.0'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'androidx.test:core:1.4.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:3.0.0'
testImplementation 'org.robolectric:robolectric:4.5.1'
Expand Down
19 changes: 14 additions & 5 deletions commons/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.geonature.commons"
android:sharedUserId="@string/sharedUserId">
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.geonature.commons"
android:sharedUserId="@string/sharedUserId">

<uses-permission android:name="fr.geonature.sync.permission.READ"/>
<permission android:name="fr.geonature.sync.permission.READ"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="fr.geonature.sync.permission.READ" />
<uses-permission android:name="fr.geonature.sync.permission.WRITE" />

<permission
android:name="fr.geonature.sync.permission.READ"
android:protectionLevel="signature" />
<permission
android:name="fr.geonature.sync.permission.WRITE"
android:protectionLevel="signature" />

</manifest>
17 changes: 5 additions & 12 deletions commons/src/main/java/fr/geonature/commons/data/helper/Provider.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package fr.geonature.commons.data.helper

import android.content.Context
import android.net.Uri
import android.net.Uri.withAppendedPath
import fr.geonature.commons.R

/**
* Base content provider.
Expand All @@ -17,12 +15,6 @@ object Provider {
*/
const val AUTHORITY = "fr.geonature.sync.provider"

/**
* Check if 'READ' permission is granted for content provider.
*/
val checkReadPermission: (Context, String?) -> Boolean =
{ context, permission -> context.getString(R.string.permission_read) == permission }

/**
* Build resource [Uri].
*/
Expand All @@ -34,9 +26,10 @@ object Provider {
val baseUri = Uri.parse("content://$AUTHORITY/$resource")

return if (path.isEmpty()) baseUri
else withAppendedPath(
baseUri,
path.asSequence().filter { it.isNotBlank() }.joinToString("/")
)
else withAppendedPath(baseUri,
path
.asSequence()
.filter { it.isNotBlank() }
.joinToString("/"))
}
}
120 changes: 120 additions & 0 deletions commons/src/main/java/fr/geonature/commons/fp/Either.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package fr.geonature.commons.fp

/**
* Represents a value of one of two possible types (a disjoint union).
* Instances of [Either] are either an instance of [Left] or [Right].
* FP Convention dictates that [Left] is used for "failure"
* and [Right] is used for "success".
*
* @see Left
* @see Right
*/
sealed class Either<out L, out R> {

/**
* Represents the left side of [Either] class which by convention is a "Failure".
*/
data class Left<out L>(val a: L) : Either<L, Nothing>()

/**
* Represents the right side of [Either] class which by convention is a "Success".
*/
data class Right<out R>(val b: R) : Either<Nothing, R>()

/**
* Returns true if this is a [Right], false otherwise.
* @see Right
*/
val isRight get() = this is Right<R>

/**
* Returns true if this is a [Left], false otherwise.
* @see Left
*/
val isLeft get() = this is Left<L>

/**
* Creates a [Left] type.
* @see Left
*/
fun <L> left(a: L) =
Left(a)

/**
* Creates a [Right] type.
* @see Right
*/
fun <R> right(b: R) =
Right(b)

/**
* Applies fnL if this is a [Left] or fnR if this is a [Right].
* @see Left
* @see Right
*/
fun fold(
fnL: (L) -> Any,
fnR: (R) -> Any
): Any =
when (this) {
is Left -> fnL(a)
is Right -> fnR(b)
}
}

/**
* Composes 2 functions.
*
* See [credits to Alex Hart.](https://proandroiddev.com/kotlins-nothing-type-946de7d464fb)
*/
fun <A, B, C> ((A) -> B).c(f: (B) -> C): (A) -> C =
{
f(this(it))
}

/**
* Right-biased flatMap() FP convention which means that [Either.Right] is assumed to be the default
* case to operate on.
* If it is [Either.Left], operations like map, flatMap, ... return the [Either.Left] value unchanged.
*/
fun <T, L, R> Either<L, R>.flatMap(fn: (R) -> Either<L, T>): Either<L, T> =
when (this) {
is Either.Left -> Either.Left(a)
is Either.Right -> fn(b)
}

/**
* Right-biased map() FP convention which means that [Either.Right] is assumed to be the default case
* to operate on.
* If it is [Either.Left], operations like map, flatMap, ... return the [Either.Left] value unchanged.
*/
fun <T, L, R> Either<L, R>.map(fn: (R) -> (T)): Either<L, T> =
this.flatMap(fn.c(::right))

/**
* Returns the value from this [Either.Right] or the given argument if this is a [Either.Left].
* Examples:
* * `Right(12).getOrElse(17)` returns 12
* * `Left(12).getOrElse(17)` returns 17
*/
fun <L, R> Either<L, R>.getOrElse(value: R): R =
when (this) {
is Either.Left -> value
is Either.Right -> b
}

/**
* Left-biased onFailure() FP convention dictates that when this class is [Either.Left], it'll perform
* the onFailure functionality passed as a parameter, but, overall will still return an either
* object so you chain calls.
*/
fun <L, R> Either<L, R>.onFailure(fn: (failure: L) -> Unit): Either<L, R> =
this.apply { if (this is Either.Left) fn(a) }

/**
* Right-biased onSuccess() FP convention dictates that when this class is [Either.Right], it'll perform
* the onSuccess functionality passed as a parameter, but, overall will still return an either
* object so you chain calls.
*/
fun <L, R> Either<L, R>.onSuccess(fn: (success: R) -> Unit): Either<L, R> =
this.apply { if (this is Either.Right) fn(b) }
12 changes: 12 additions & 0 deletions commons/src/main/java/fr/geonature/commons/fp/Failure.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package fr.geonature.commons.fp

/**
* Base class for handling errors/failures/exceptions.
* Every feature specific failure should extend [FeatureFailure] class.
*/
sealed class Failure {
object NetworkFailure : Failure()
object ServerFailure : Failure()

abstract class FeatureFailure : Failure()
}
40 changes: 34 additions & 6 deletions commons/src/main/java/fr/geonature/commons/input/AbstractInput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ abstract class AbstractInput(

var id: Long = generateId()
var date: Date = Date()
var status: Status = Status.DRAFT
var datasetId: Long? = null
private val inputObserverIds: MutableSet<Long> = mutableSetOf()
private val inputTaxa: MutableMap<Long, AbstractInputTaxon> = LinkedHashMap()
Expand All @@ -31,7 +32,16 @@ abstract class AbstractInput(
constructor(source: Parcel) : this(source.readString()!!) {
this.id = source.readLong()
this.date = source.readSerializable() as Date
this.datasetId = source.readLong()
this.status = source
.readString()
.let { statusAsString ->
Status
.values()
.firstOrNull { it.name == statusAsString }
?: Status.DRAFT
}
this.datasetId = source
.readLong()
.takeIf { it != -1L }

val inputObserverId = source.readLong()
Expand Down Expand Up @@ -61,9 +71,17 @@ abstract class AbstractInput(
it.writeString(module)
it.writeLong(this.id)
it.writeSerializable(this.date)
it.writeLong(this.datasetId ?: -1L)
it.writeString(this.status.name)
it.writeLong(
this.datasetId
?: -1L
)
it.writeLong(if (inputObserverIds.isEmpty()) -1 else inputObserverIds.first())
it.writeLongArray(inputObserverIds.drop(1).toLongArray())
it.writeLongArray(
inputObserverIds
.drop(1)
.toLongArray()
)
it.writeTypedList(getInputTaxa())
}
}
Expand All @@ -77,6 +95,7 @@ abstract class AbstractInput(
if (module != other.module) return false
if (id != other.id) return false
if (date != other.date) return false
if (status != other.status) return false
if (datasetId != other.datasetId) return false
if (inputObserverIds != other.inputObserverIds) return false
if (inputTaxa != other.inputTaxa) return false
Expand All @@ -88,6 +107,7 @@ abstract class AbstractInput(
var result = module.hashCode()
result = 31 * result + id.hashCode()
result = 31 * result + date.hashCode()
result = 31 * result + status.hashCode()
result = 31 * result + datasetId.hashCode()
result = 31 * result + inputObserverIds.hashCode()
result = 31 * result + inputTaxa.hashCode()
Expand All @@ -96,7 +116,8 @@ abstract class AbstractInput(
}

fun setDate(isoDate: String?) {
this.date = toDate(isoDate) ?: Date()
this.date = toDate(isoDate)
?: Date()
}

/**
Expand All @@ -117,7 +138,8 @@ abstract class AbstractInput(
* Gets only selected input observers without the primary input observer.
*/
fun getInputObserverIds(): Set<Long> {
return this.inputObserverIds.drop(1)
return this.inputObserverIds
.drop(1)
.toSet()
}

Expand All @@ -126,7 +148,8 @@ abstract class AbstractInput(
}

fun setPrimaryInputObserverId(id: Long) {
val inputObservers = this.inputObserverIds.toMutableList()
val inputObservers = this.inputObserverIds
.toMutableList()
.apply {
add(
0,
Expand Down Expand Up @@ -204,6 +227,11 @@ abstract class AbstractInput(

abstract fun getTaxaFromParcel(source: Parcel): List<AbstractInputTaxon>

enum class Status {
DRAFT,
TO_SYNC
}

/**
* Generates a pseudo unique ID. The value is the number of seconds since Jan. 1, 2016, midnight.
*
Expand Down
Loading

0 comments on commit 7487203

Please sign in to comment.