Change/move some gradle modules to be better separated. Let core module be a kotlin lib.

This commit is contained in:
ganfra
2023-01-24 17:38:57 +01:00
parent 0d5f348354
commit 27bf42c5f2
59 changed files with 314 additions and 202 deletions

4
.idea/compiler.xml generated
View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="1.8">
<module name="ElementX.libraries.composeutils" target="11" />
</bytecodeTargetLevel>
</component>
</project>

2
.idea/misc.xml generated
View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -34,14 +34,12 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.di)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.elementresources)
implementation(projects.libraries.testtags)
implementation(libs.appyx.core)
implementation(projects.libraries.uiStrings)
ksp(libs.showkase.processor)
testImplementation(libs.test.junit)

View File

@@ -53,7 +53,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.compose.textFieldState
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.components.VectorIcon
import io.element.android.features.login.R
import io.element.android.features.login.error.changeServerError

View File

@@ -29,7 +29,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode
import io.element.android.libraries.architecture.presenterConnector
import io.element.android.libraries.core.compose.OnLifecycleEvent
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.di.AppScope
@ContributesNode(AppScope::class)

View File

@@ -58,7 +58,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.core.compose.textFieldState
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.features.login.error.loginError
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag

View File

@@ -33,7 +33,6 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.di)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.matrix)

View File

@@ -33,14 +33,12 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.di)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.textcomposer)
implementation(libs.appyx.core)
implementation(libs.coil.compose)
implementation(libs.datetime)
implementation(libs.accompanist.flowlayout)

View File

@@ -56,7 +56,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import io.element.android.libraries.core.compose.LogCompositions
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.features.messages.actionlist.ActionListEvents
@@ -65,6 +64,7 @@ import io.element.android.features.messages.actionlist.model.TimelineItemAction
import io.element.android.features.messages.timeline.model.TimelineItem
import io.element.android.features.messages.textcomposer.MessageComposerView
import io.element.android.features.messages.timeline.TimelineView
import io.element.android.libraries.designsystem.utils.LogCompositions
import kotlinx.coroutines.launch
import timber.log.Timber

View File

@@ -58,7 +58,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import io.element.android.libraries.core.compose.PairCombinedPreviewParameter
import io.element.android.libraries.designsystem.utils.PairCombinedPreviewParameter
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.features.messages.timeline.model.AggregatedReaction

View File

@@ -34,7 +34,6 @@ dependencies {
implementation(projects.libraries.testtags)
implementation(libs.accompanist.pager)
implementation(libs.accompanist.pagerindicator)
implementation(libs.appyx.core)
testImplementation(libs.test.junit)
androidTestImplementation(libs.test.junitext)
ksp(libs.showkase.processor)

View File

@@ -34,7 +34,6 @@ anvil {
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
implementation(projects.libraries.di)
implementation(projects.libraries.architecture)
implementation(projects.libraries.core)
implementation(projects.libraries.matrixui)

View File

@@ -32,11 +32,11 @@ anvil {
}
dependencies {
implementation(projects.libraries.core)
anvil(projects.anvilcodegen)
implementation(projects.libraries.di)
implementation(projects.libraries.architecture)
implementation(projects.anvilannotations)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.elementresources)
implementation(projects.libraries.uiStrings)

View File

@@ -51,10 +51,10 @@ import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.core.compose.LogCompositions
import io.element.android.libraries.core.compose.textFieldState
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.components.LabelledCheckbox
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.R as StringR
@OptIn(ExperimentalMaterial3Api::class)
@@ -111,7 +111,9 @@ fun BugReportView(
.padding(horizontal = 16.dp, vertical = 16.dp),
fontSize = 16.sp,
)
var descriptionFieldState by textFieldState(stateValue = state.formState.description)
var descriptionFieldState by textFieldState(
stateValue = state.formState.description
)
Column(
// modifier = Modifier.weight(1f),
) {

View File

@@ -19,8 +19,8 @@ package io.element.android.features.rageshake.crash.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import io.element.android.libraries.core.compose.LogCompositions
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.R as StringR
@Composable
@@ -28,7 +28,10 @@ fun CrashDetectionView(
state: CrashDetectionState,
onOpenBugReport: () -> Unit = { },
) {
LogCompositions(tag = "Crash", msg = "CrashDetectionScreen")
LogCompositions(
tag = "Crash",
msg = "CrashDetectionScreen"
)
fun onPopupDismissed() {
state.eventSink(CrashDetectionEvents.ResetAllCrashData)

View File

@@ -16,7 +16,7 @@
package io.element.android.features.rageshake.detection
import io.element.android.libraries.core.screenshot.ImageResult
import io.element.android.features.rageshake.screenshot.ImageResult
sealed interface RageshakeDetectionEvents {
object Dismiss : RageshakeDetectionEvents

View File

@@ -23,12 +23,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.screenshot.ImageResult
import io.element.android.features.rageshake.preferences.RageshakePreferencesEvents
import io.element.android.features.rageshake.preferences.RageshakePreferencesPresenter
import io.element.android.features.rageshake.rageshake.RageShake
import io.element.android.features.rageshake.screenshot.ImageResult
import io.element.android.features.rageshake.screenshot.ScreenshotHolder
import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber

View File

@@ -23,12 +23,12 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.Lifecycle
import io.element.android.libraries.core.compose.LogCompositions
import io.element.android.libraries.core.compose.OnLifecycleEvent
import io.element.android.libraries.core.hardware.vibrate
import io.element.android.libraries.core.screenshot.ImageResult
import io.element.android.libraries.core.screenshot.screenshot
import io.element.android.features.rageshake.screenshot.ImageResult
import io.element.android.features.rageshake.screenshot.screenshot
import io.element.android.libraries.androidutils.hardware.vibrate
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.R as StringR
@Composable
@@ -36,7 +36,10 @@ fun RageshakeDetectionView(
state: RageshakeDetectionState,
onOpenBugReport: () -> Unit = { },
) {
LogCompositions(tag = "Rageshake", msg = "RageshakeDetectionScreen")
LogCompositions(
tag = "Rageshake",
msg = "RageshakeDetectionScreen"
)
val eventSink = state.eventSink
val context = LocalContext.current
OnLifecycleEvent { _, event ->

View File

@@ -19,17 +19,17 @@ package io.element.android.features.rageshake.logs
import android.content.Context
import android.util.Log
import io.element.android.libraries.core.data.tryOrNull
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.util.logging.FileHandler
import java.util.logging.Level
import java.util.logging.Logger
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
/**
* Will be planted in Timber.
@@ -85,7 +85,9 @@ class VectorFileLogger(
tryOrNull { file.delete() }
}
fileHandler = tryOrNull("Failed to initialize FileLogger") {
fileHandler = tryOrNull(
onError = { Timber.e(it, "Failed to initialize FileLogger") }
) {
FileHandler(
cacheDirectory.absolutePath + "/" + fileNamePrefix + ".%g.txt",
maxLogSizeByte,
@@ -134,7 +136,9 @@ class VectorFileLogger(
* @return The list of files with logs.
*/
fun getLogFiles(): List<File> {
return tryOrNull("## getLogFiles() failed") {
return tryOrNull(
onError = { Timber.e(it, "## getLogFiles() failed") }
) {
fileHandler
?.flush()
?.let { 0 until logRotationCount }

View File

@@ -19,7 +19,7 @@ package io.element.android.features.rageshake.reporter
import android.content.Context
import android.os.Build
import io.element.android.libraries.core.extensions.toOnOff
import io.element.android.libraries.core.file.compressFile
import io.element.android.libraries.androidutils.file.compressFile
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.ApplicationContext
import io.element.android.features.rageshake.R

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.screenshot
package io.element.android.features.rageshake.screenshot
import android.app.Activity
import android.graphics.Bitmap

View File

@@ -18,7 +18,7 @@ package io.element.android.features.rageshake.screenshot
import android.content.Context
import android.graphics.Bitmap
import io.element.android.libraries.core.bitmap.writeBitmap
import io.element.android.libraries.androidutils.bitmap.writeBitmap
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn

View File

@@ -34,13 +34,11 @@ dependencies {
anvil(projects.anvilcodegen)
implementation(projects.anvilannotations)
implementation(projects.libraries.di)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
implementation(libs.appyx.core)
implementation(projects.libraries.elementresources)
implementation(projects.libraries.uiStrings)
implementation(libs.datetime)

View File

@@ -36,7 +36,6 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Velocity
import io.element.android.libraries.core.compose.LogCompositions
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.features.roomlist.components.RoomListTopBar
import io.element.android.features.roomlist.components.RoomSummaryRow
@@ -44,6 +43,7 @@ import io.element.android.features.roomlist.model.RoomListEvents
import io.element.android.features.roomlist.model.RoomListRoomSummary
import io.element.android.features.roomlist.model.RoomListState
import io.element.android.features.roomlist.model.stubbedRoomSummaries
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.matrix.core.RoomId
import io.element.android.libraries.matrix.core.UserId
import io.element.android.libraries.matrix.ui.model.MatrixUser
@@ -103,7 +103,10 @@ fun RoomListView(
}
}
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
LogCompositions(tag = "RoomListScreen", msg = "Content")
LogCompositions(
tag = "RoomListScreen",
msg = "Content"
)
val nestedScrollConnection = remember {
object : NestedScrollConnection {

View File

@@ -52,9 +52,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import io.element.android.libraries.core.compose.LogCompositions
import io.element.android.libraries.core.compose.textFieldState
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.form.textFieldState
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.matrix.ui.model.MatrixUser
import io.element.android.libraries.ui.strings.R as StringR
@@ -66,7 +66,10 @@ fun RoomListTopBar(
onOpenSettings: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior
) {
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
LogCompositions(
tag = "RoomListScreen",
msg = "TopBar"
)
var searchWidgetStateIsOpened by rememberSaveable { mutableStateOf(false) }
fun closeFilter() {

View File

@@ -35,7 +35,6 @@ dependencies {
anvil(projects.anvilcodegen)
implementation(projects.anvilannotations)
implementation(projects.libraries.di)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix)
@@ -44,8 +43,6 @@ dependencies {
implementation(projects.libraries.elementresources)
implementation(projects.libraries.uiStrings)
implementation(libs.appyx.core)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)

View File

@@ -114,6 +114,7 @@ showkase_processor = { module = "com.airbnb.android:showkase-processor", version
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }
timber = "com.jakewharton.timber:timber:5.0.1"
# Di
inject = "javax.inject:javax.inject:1"

1
libraries/androidutils/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "io.element.android.libraries.androidutils"
compileSdk = 32
defaultConfig {
minSdk = 21
targetSdk = 32
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation(libs.timber)
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.androidutils
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.element.android.libraries.androidutils.test", appContext.packageName)
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 New Vector Ltd
~ Copyright (c) 2023 New Vector Ltd
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -16,7 +16,5 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.VIBRATE" />
</manifest>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.bitmap
package io.element.android.libraries.androidutils.bitmap
import android.graphics.Bitmap
import java.io.File

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.androidutils.extensions
import android.util.Patterns
/**
* Check if a CharSequence is an email.
*/
fun CharSequence.isEmail() = Patterns.EMAIL_ADDRESS.matcher(this).matches()

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
package io.element.android.libraries.core.file
package io.element.android.libraries.androidutils.file
import timber.log.Timber
import java.io.File
import java.util.zip.GZIPOutputStream
import timber.log.Timber
/**
* GZip a file.

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.hardware
package io.element.android.libraries.androidutils.hardware
import android.content.Context
import android.os.Build

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.core.ui
package io.element.android.libraries.androidutils.ui
import android.content.res.Resources
import android.util.TypedValue

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.ui
package io.element.android.libraries.androidutils.ui
import android.view.View
import android.view.inputmethod.InputMethodManager

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.androidutils
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@@ -1,3 +1,4 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
@@ -14,10 +15,18 @@
* limitations under the License.
*/
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id("io.element.android-compose-library")
id("java-library")
alias(libs.plugins.kotlin.jvm)
}
android {
namespace = "io.element.android.libraries.core"
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation(libs.coroutines.core)
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,104 +0,0 @@
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.core.coroutine
import android.os.SystemClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
@ExperimentalCoroutinesApi
fun <T> Flow<T>.chunk(durationInMillis: Long): Flow<List<T>> {
require(durationInMillis > 0) { "Duration should be greater than 0" }
return flow {
coroutineScope {
val events = ArrayList<T>()
val ticker = fixedPeriodTicker(durationInMillis)
try {
val upstreamValues = produce(capacity = Channel.CONFLATED) {
collect { value -> send(value) }
}
while (isActive) {
var hasTimedOut = false
select<Unit> {
upstreamValues.onReceive {
events.add(it)
}
ticker.onReceive {
hasTimedOut = true
}
}
if (hasTimedOut && events.isNotEmpty()) {
emit(events.toList())
events.clear()
}
}
} catch (e: ClosedReceiveChannelException) {
// drain remaining events
if (events.isNotEmpty()) emit(events.toList())
} finally {
ticker.cancel()
}
}
}
}
@ExperimentalCoroutinesApi
fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
var windowStartTime = SystemClock.elapsedRealtime()
var emitted = false
collect { value ->
val currentTime = SystemClock.elapsedRealtime()
val delta = currentTime - windowStartTime
if (delta >= windowDuration) {
windowStartTime += delta / windowDuration * windowDuration
emitted = false
}
if (!emitted) {
emit(value)
emitted = true
}
}
}
@ExperimentalCoroutinesApi
fun tickerFlow(scope: CoroutineScope, delayMillis: Long, initialDelayMillis: Long = delayMillis): Flow<Unit> {
return scope.fixedPeriodTicker(delayMillis, initialDelayMillis).consumeAsFlow()
}
@ExperimentalCoroutinesApi
private fun CoroutineScope.fixedPeriodTicker(delayMillis: Long, initialDelayMillis: Long = delayMillis): ReceiveChannel<Unit> {
require(delayMillis >= 0) { "Expected non-negative delay, but has $delayMillis ms" }
require(initialDelayMillis >= 0) { "Expected non-negative initial delay, but has $initialDelayMillis ms" }
return produce(capacity = 0) {
delay(initialDelayMillis)
while (true) {
channel.send(Unit)
delay(delayMillis)
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -16,15 +16,11 @@
package io.element.android.libraries.core.data
import timber.log.Timber
inline fun <A> tryOrNull(message: String? = null, operation: () -> A): A? {
inline fun <A> tryOrNull(noinline onError: ((Throwable) -> Unit)? = null, operation: () -> A): A? {
return try {
operation()
} catch (any: Throwable) {
if (message != null) {
Timber.e("TAG", message, any)
}
onError?.invoke(any)
null
}
}

View File

@@ -16,19 +16,11 @@
package io.element.android.libraries.core.extensions
import android.util.Patterns
fun Boolean.toOnOff() = if (this) "ON" else "OFF"
inline fun <T> T.ooi(block: (T) -> Unit): T = also(block)
/**
* Check if a CharSequence is an email.
*/
fun CharSequence.isEmail() = Patterns.EMAIL_ADDRESS.matcher(this).matches()
// fun CharSequence.isMatrixId() = MatrixPatterns.isUserId(this.toString())
/**
* Return empty CharSequence if the CharSequence is null.
*/

1
libraries/coroutines/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// TODO: Remove once https://youtrack.jetbrains.com/issue/KTIJ-19369 is fixed
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
id("java-library")
alias(libs.plugins.kotlin.jvm)
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation(libs.coroutines.core)
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.compose
package io.element.android.libraries.designsystem.components.form
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.compose
package io.element.android.libraries.designsystem.components.keyboard
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.WindowInsets

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
package io.element.android.libraries.core.compose
package io.element.android.libraries.designsystem.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember
import io.element.android.libraries.core.BuildConfig
import io.element.android.libraries.designsystem.BuildConfig
import timber.log.Timber
// Note the inline function below which ensures that this function is essentially

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.compose
package io.element.android.libraries.designsystem.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package io.element.android.libraries.core.compose
package io.element.android.libraries.designsystem.utils
import androidx.compose.ui.tooling.preview.PreviewParameterProvider

View File

@@ -26,7 +26,6 @@ import io.element.android.libraries.matrix.room.MatrixRoom
import io.element.android.libraries.matrix.room.RoomSummaryDataSource
import io.element.android.libraries.matrix.room.RustMatrixRoom
import io.element.android.libraries.matrix.room.RustRoomSummaryDataSource
import io.element.android.libraries.matrix.session.PreferencesSessionStore
import io.element.android.libraries.matrix.session.SessionStore
import io.element.android.libraries.matrix.session.sessionId
import io.element.android.libraries.matrix.sync.SlidingSyncObserverProxy

View File

@@ -32,6 +32,7 @@ android {
dependencies {
implementation(projects.libraries.elementresources)
implementation(projects.libraries.uiStrings)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.matrix)
implementation(libs.wysiwyg)

View File

@@ -40,14 +40,13 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.google.android.material.shape.MaterialShapeDrawable
import io.element.android.wysiwyg.EditorEditText
import io.element.android.wysiwyg.inputhandlers.models.InlineFormat
import io.element.android.libraries.core.ui.DimensionConverter
import io.element.android.libraries.core.ui.hideKeyboard
import io.element.android.libraries.core.ui.showKeyboard
import io.element.android.libraries.androidutils.ui.hideKeyboard
import io.element.android.libraries.androidutils.ui.showKeyboard
import io.element.android.libraries.textcomposer.databinding.ComposerRichTextLayoutBinding
import io.element.android.libraries.textcomposer.databinding.ViewRichTextMenuButtonBinding
import io.element.android.libraries.textcomposer.tools.setTextIfDifferent
import io.element.android.wysiwyg.EditorEditText
import io.element.android.wysiwyg.inputhandlers.models.InlineFormat
import uniffi.wysiwyg_composer.ActionState
import uniffi.wysiwyg_composer.ComposerAction
import io.element.android.element.resources.R as ElementR
@@ -118,7 +117,8 @@ class RichTextComposerLayout @JvmOverloads constructor(
}
}
private val dimensionConverter = DimensionConverter(resources)
private val dimensionConverter =
io.element.android.libraries.androidutils.ui.DimensionConverter(resources)
fun setFullScreen(isFullScreen: Boolean, animated: Boolean, manageKeyboard: Boolean) {
if (!animated && views.composerLayout.layoutParams != null) {

View File

@@ -60,3 +60,4 @@ include(":anvilcodegen")
include(":libraries:architecture")
include(":libraries:matrixtest")
include(":features:template")
include(":libraries:androidutils")