├── .gitignore ├── README.md ├── app ├── .DS_Store ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── .DS_Store │ ├── androidTest │ └── java │ │ └── ai │ │ └── wordbox │ │ └── dogsembeddings │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── .DS_Store │ ├── AndroidManifest.xml │ ├── assets │ │ └── universal_sentence_encoder.tflite │ ├── java │ │ └── ai │ │ │ └── wordbox │ │ │ ├── .DS_Store │ │ │ └── dogsembeddings │ │ │ ├── MainActivity.kt │ │ │ ├── MediaPipeEmbeddings.kt │ │ │ ├── TextEmbeddingsViewModel.kt │ │ │ └── ui │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── mipmap-anydpi │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── ai │ └── wordbox │ └── dogsembeddings │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── embeddings app.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | .DS_Store 5 | 6 | # Local configuration file (sdk path, etc) 7 | local.properties 8 | 9 | # Log/OS Files 10 | *.log 11 | 12 | # Android Studio generated files and folders 13 | captures/ 14 | .externalNativeBuild/ 15 | .cxx/ 16 | *.apk 17 | output.json 18 | 19 | # IntelliJ 20 | *.iml 21 | .idea/ 22 | misc.xml 23 | deploymentTargetDropDown.xml 24 | render.experimental.xml 25 | 26 | # Keystore files 27 | *.jks 28 | *.keystore 29 | 30 | # Google Services (e.g. APIs or Firebase) 31 | google-services.json 32 | 33 | # Android Profiling 34 | *.hprof 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Text Embeddings - MediaPipe with Android-Kotlin 2 | 3 | An embedding is a relatively low-dimensional space into which you can translate high-dimensional vectors. Embeddings make it easier to do machine learning on large inputs like sparse vectors representing words. Ideally, an embedding captures some of the semantics of the input by placing semantically similar inputs close together in the embedding space. An embedding can be learned and reused across models. 4 | [Embeddings Ref](https://developers.google.com/machine-learning/crash-course/embeddings/video-lecture) 5 | 6 | Embeddings are commonly used for [OpenAI Ref](https://platform.openai.com/docs/guides/embeddings): 7 | - Search 8 | - Clustering (grouped by similarity) 9 | - Recommendations 10 | - Anomaly detection 11 | 12 | [MediaPipe](https://developers.google.com/mediapipe) is a set of On-Device Machine Learning libraries ready for deployment in production. There are libraries for Android, iOS, Web, and Python. One of the multiple solutions is to create a numerical representation of text data, this means the embeddings. 13 | 14 | This is an open-source code used MediaPipe for creating the embeddings and cosine as a similarity measure with Kotlin - Android. 15 | 16 | Try it: 17 | 18 | 19 | 20 | 21 | Made with ❤ by [jggomez](https://devhack.co). 22 | 23 | [![Twitter Badge](https://img.shields.io/badge/-@jggomezt-1ca0f1?style=flat-square&labelColor=1ca0f1&logo=twitter&logoColor=white&link=https://twitter.com/jggomezt)](https://twitter.com/jggomezt) 24 | [![Linkedin Badge](https://img.shields.io/badge/-jggomezt-blue?style=flat-square&logo=Linkedin&logoColor=white&link=https://www.linkedin.com/in/jggomezt/)](https://www.linkedin.com/in/jggomezt/) 25 | [![Medium Badge](https://img.shields.io/badge/-@jggomezt-03a57a?style=flat-square&labelColor=000000&logo=Medium&link=https://medium.com/@jggomezt)](https://medium.com/@jggomezt) 26 | 27 | ## License 28 | 29 | Copyright 2024 Juan Guillermo Gómez 30 | 31 | Licensed under the Apache License, Version 2.0 (the "License"); 32 | you may not use this file except in compliance with the License. 33 | You may obtain a copy of the License at 34 | 35 | http://www.apache.org/licenses/LICENSE-2.0 36 | 37 | Unless required by applicable law or agreed to in writing, software 38 | distributed under the License is distributed on an "AS IS" BASIS, 39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 | See the License for the specific language governing permissions and 41 | limitations under the License. 42 | -------------------------------------------------------------------------------- /app/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/.DS_Store -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("org.jetbrains.kotlin.android") 4 | } 5 | 6 | android { 7 | namespace = "ai.wordbox.dogsembeddings" 8 | compileSdk = 34 9 | 10 | defaultConfig { 11 | applicationId = "ai.wordbox.dogsembeddings" 12 | minSdk = 30 13 | targetSdk = 34 14 | versionCode = 1 15 | versionName = "1.0" 16 | 17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary = true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | isMinifyEnabled = false 26 | proguardFiles( 27 | getDefaultProguardFile("proguard-android-optimize.txt"), 28 | "proguard-rules.pro" 29 | ) 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility = JavaVersion.VERSION_1_8 34 | targetCompatibility = JavaVersion.VERSION_1_8 35 | } 36 | kotlinOptions { 37 | jvmTarget = "1.8" 38 | } 39 | buildFeatures { 40 | compose = true 41 | } 42 | composeOptions { 43 | kotlinCompilerExtensionVersion = "1.4.3" 44 | } 45 | packaging { 46 | resources { 47 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 48 | } 49 | } 50 | } 51 | 52 | dependencies { 53 | 54 | implementation("androidx.core:core-ktx:1.12.0") 55 | implementation ("com.google.mediapipe:tasks-text:0.20230731") 56 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") 57 | implementation("androidx.activity:activity-compose:1.8.0") 58 | implementation(platform("androidx.compose:compose-bom:2023.03.00")) 59 | implementation("androidx.compose.ui:ui") 60 | implementation("androidx.compose.ui:ui-graphics") 61 | implementation("androidx.compose.ui:ui-tooling-preview") 62 | implementation("androidx.compose.material3:material3") 63 | testImplementation("junit:junit:4.13.2") 64 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 65 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 66 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00")) 67 | androidTestImplementation("androidx.compose.ui:ui-test-junit4") 68 | debugImplementation("androidx.compose.ui:ui-tooling") 69 | debugImplementation("androidx.compose.ui:ui-test-manifest") 70 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/.DS_Store -------------------------------------------------------------------------------- /app/src/androidTest/java/ai/wordbox/dogsembeddings/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("ai.wordbox.dogsembeddings", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/.DS_Store -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/assets/universal_sentence_encoder.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/assets/universal_sentence_encoder.tflite -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/java/ai/wordbox/.DS_Store -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/dogsembeddings/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings 2 | 3 | import ai.wordbox.dogsembeddings.ui.theme.DogsEmbeddingsTheme 4 | import android.os.Bundle 5 | import androidx.activity.ComponentActivity 6 | import androidx.activity.compose.setContent 7 | import androidx.activity.viewModels 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.Spacer 10 | import androidx.compose.foundation.layout.fillMaxSize 11 | import androidx.compose.foundation.layout.fillMaxWidth 12 | import androidx.compose.foundation.layout.height 13 | import androidx.compose.foundation.layout.padding 14 | import androidx.compose.foundation.rememberScrollState 15 | import androidx.compose.foundation.verticalScroll 16 | import androidx.compose.material3.Button 17 | import androidx.compose.material3.CircularProgressIndicator 18 | import androidx.compose.material3.ExperimentalMaterial3Api 19 | import androidx.compose.material3.MaterialTheme 20 | import androidx.compose.material3.OutlinedTextField 21 | import androidx.compose.material3.Surface 22 | import androidx.compose.material3.Text 23 | import androidx.compose.runtime.Composable 24 | import androidx.compose.runtime.LaunchedEffect 25 | import androidx.compose.runtime.getValue 26 | import androidx.compose.runtime.mutableStateOf 27 | import androidx.compose.runtime.remember 28 | import androidx.compose.runtime.setValue 29 | import androidx.compose.ui.Alignment 30 | import androidx.compose.ui.Modifier 31 | import androidx.compose.ui.platform.LocalContext 32 | import androidx.compose.ui.tooling.preview.Preview 33 | import androidx.compose.ui.unit.dp 34 | 35 | class MainActivity : ComponentActivity() { 36 | 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | val textEmbeddingsViewModel: TextEmbeddingsViewModel by viewModels() 40 | 41 | setContent { 42 | DogsEmbeddingsTheme { 43 | Surface( 44 | modifier = Modifier 45 | .fillMaxSize() 46 | .padding(16.dp), 47 | color = MaterialTheme.colorScheme.background 48 | ) { 49 | val context = LocalContext.current 50 | 51 | LaunchedEffect(Unit) { 52 | textEmbeddingsViewModel.setUpMLModel(context) 53 | } 54 | 55 | Body( 56 | sentences = textEmbeddingsViewModel 57 | .uiStateTextEmbeddings 58 | .sentences, 59 | similaritySentences = textEmbeddingsViewModel 60 | .uiStateTextEmbeddings 61 | .similaritySentences, 62 | loading = textEmbeddingsViewModel 63 | .uiStateTextEmbeddings 64 | .state == State.Loading, 65 | error = textEmbeddingsViewModel 66 | .uiStateTextEmbeddings 67 | .errorMessage 68 | ) { 69 | textEmbeddingsViewModel.calculateSimilarity( 70 | mainSentence = it, 71 | sentences = textEmbeddingsViewModel 72 | .uiStateTextEmbeddings 73 | .sentences 74 | ) 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | @Composable 83 | fun Body( 84 | sentences: List, 85 | similaritySentences: List, 86 | modifier: Modifier = Modifier, 87 | loading: Boolean = false, 88 | error: String = String(), 89 | onClickFindSimilarities: (text: String) -> Unit 90 | ) { 91 | var text by remember { mutableStateOf("") } 92 | 93 | Column(modifier = modifier) { 94 | SentencesList(sentences = sentences) 95 | Spacer(modifier = Modifier.height(20.dp)) 96 | InputUserText(text = text) { 97 | text = it 98 | } 99 | Spacer(modifier = Modifier.height(20.dp)) 100 | Button( 101 | onClick = { onClickFindSimilarities(text) }, 102 | modifier = Modifier.align(Alignment.CenterHorizontally) 103 | ) { 104 | when (loading) { 105 | false -> Text(text = "Find Similarity Sentences") 106 | true -> CircularProgressIndicator( 107 | color = MaterialTheme.colorScheme.surfaceVariant, 108 | ) 109 | } 110 | } 111 | Spacer(modifier = Modifier.height(20.dp)) 112 | when { 113 | error.isBlank() -> { 114 | SentencesList(sentences = similaritySentences.map { 115 | "${it.sentence} - ${it.resultSimilarity}" 116 | }) 117 | } 118 | 119 | else -> Text(text = error, modifier = Modifier.fillMaxSize()) 120 | } 121 | } 122 | } 123 | 124 | @Composable 125 | fun SentencesList( 126 | sentences: List, 127 | modifier: Modifier = Modifier 128 | ) { 129 | Column( 130 | modifier = Modifier 131 | .fillMaxWidth() 132 | .height(250.dp) 133 | .verticalScroll(rememberScrollState()) 134 | ) { 135 | sentences.forEachIndexed { index, sentence -> 136 | Text( 137 | text = "${index + 1} -> $sentence", 138 | modifier = modifier 139 | ) 140 | } 141 | } 142 | } 143 | 144 | @OptIn(ExperimentalMaterial3Api::class) 145 | @Composable 146 | fun InputUserText( 147 | text: String, 148 | modifier: Modifier = Modifier, 149 | onValueChanged: (text: String) -> Unit, 150 | ) { 151 | OutlinedTextField( 152 | value = text, 153 | modifier = modifier.fillMaxWidth(), 154 | onValueChange = onValueChanged 155 | ) 156 | } 157 | 158 | @Preview(showBackground = true) 159 | @Composable 160 | fun GreetingPreview() { 161 | Body( 162 | sentences = listOf( 163 | "What is your name?", 164 | "I'm good", 165 | "The next weekend will be your birthday" 166 | ), 167 | similaritySentences = listOf( 168 | SentenceSimilarity( 169 | mainSentence = "hi", 170 | sentence = "asasasas", 171 | mainSentenceEmbeddings = "asasasasaasas", 172 | sentenceEmbeddings = "asasasasaasas", 173 | resultSimilarity = 40.0 174 | ), 175 | SentenceSimilarity( 176 | mainSentence = "hi", 177 | sentence = "asasasas", 178 | mainSentenceEmbeddings = "asasasasaasas", 179 | sentenceEmbeddings = "asasasasaasas", 180 | resultSimilarity = 40.0 181 | ), 182 | ) 183 | ) { 184 | 185 | } 186 | } -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/dogsembeddings/MediaPipeEmbeddings.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import com.google.mediapipe.tasks.core.BaseOptions 6 | import com.google.mediapipe.tasks.core.Delegate 7 | import com.google.mediapipe.tasks.text.textembedder.TextEmbedder 8 | import kotlin.coroutines.resume 9 | import kotlin.coroutines.resumeWithException 10 | import kotlinx.coroutines.suspendCancellableCoroutine 11 | 12 | data class SentenceSimilarity( 13 | val mainSentence: String, 14 | val sentence: String, 15 | val mainSentenceEmbeddings: String, 16 | val sentenceEmbeddings: String, 17 | val resultSimilarity: Double, 18 | ) 19 | 20 | class MediaPipeEmbeddings { 21 | 22 | companion object { 23 | const val MODEL_NAME = "universal_sentence_encoder.tflite" 24 | const val MIN_SIMILARITY_VALUE = 0.8 25 | } 26 | 27 | private lateinit var textEmbedder: TextEmbedder 28 | 29 | suspend fun setUpMLModel(context: Context) { 30 | suspendCancellableCoroutine { continuation -> 31 | try { 32 | val baseOptions = BaseOptions 33 | .builder() 34 | .setModelAssetPath(MODEL_NAME) 35 | .setDelegate(Delegate.CPU) 36 | .build() 37 | val optionsBuilder = 38 | TextEmbedder.TextEmbedderOptions.builder().setBaseOptions(baseOptions) 39 | val options = optionsBuilder.build() 40 | textEmbedder = TextEmbedder.createFromOptions(context, options) 41 | continuation.resume(true) 42 | } catch (e: Exception) { 43 | continuation.resumeWithException(e) 44 | } 45 | } 46 | } 47 | 48 | suspend fun getSimilarities( 49 | mainSentence: String, 50 | sentences: List 51 | ): List { 52 | return suspendCancellableCoroutine { continuation -> 53 | try { 54 | textEmbedder.let { 55 | Log.i("TextEmbeddingsViewModel", "Main Sentence => $mainSentence") 56 | val mainSentenceEmbed = getEmbeddings(mainSentence) 57 | 58 | val similaritySentences: MutableList = 59 | ArrayList().apply { 60 | sentences.forEach { 61 | val sentenceEmbed = getEmbeddings(it) 62 | val similarity = 63 | TextEmbedder.cosineSimilarity(mainSentenceEmbed, sentenceEmbed) 64 | Log.i( 65 | "TextEmbeddingsViewModel", 66 | "Embeddings Main Sentence => $mainSentence" 67 | ) 68 | Log.i( 69 | "TextEmbeddingsViewModel", 70 | "Embeddings Another Sentence => $sentenceEmbed" 71 | ) 72 | if (similarity > MIN_SIMILARITY_VALUE) { 73 | this.add( 74 | SentenceSimilarity( 75 | mainSentence = mainSentence, 76 | sentence = it, 77 | mainSentenceEmbeddings = mainSentenceEmbed.toString(), 78 | sentenceEmbeddings = sentenceEmbed.toString(), 79 | resultSimilarity = similarity 80 | ) 81 | ) 82 | } 83 | } 84 | } 85 | 86 | similaritySentences.sortByDescending { it.resultSimilarity } 87 | continuation.resume(similaritySentences) 88 | } 89 | } catch (e: Exception) { 90 | continuation.resumeWithException(e) 91 | } 92 | } 93 | } 94 | 95 | private fun getEmbeddings(sentence: String) = textEmbedder 96 | .embed(sentence) 97 | .embeddingResult() 98 | .embeddings() 99 | .first() 100 | 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/dogsembeddings/TextEmbeddingsViewModel.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings 2 | 3 | import android.content.Context 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import androidx.lifecycle.ViewModel 8 | import androidx.lifecycle.viewModelScope 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | 12 | sealed class State { 13 | object Loading : State() 14 | object Success : State() 15 | object Error : State() 16 | object Empty : State() 17 | } 18 | 19 | data class TextEmbeddingsUiState( 20 | val sentences: List = emptyList(), 21 | val similaritySentences: List = emptyList(), 22 | val state: State = State.Empty, 23 | val errorMessage: String = String() 24 | ) 25 | 26 | class TextEmbeddingsViewModel : ViewModel() { 27 | 28 | private lateinit var mediaPipeEmbeddings: MediaPipeEmbeddings 29 | 30 | var uiStateTextEmbeddings by mutableStateOf(TextEmbeddingsUiState(state = State.Empty)) 31 | private set 32 | 33 | fun setUpMLModel(context: Context) { 34 | mediaPipeEmbeddings = MediaPipeEmbeddings() 35 | uiStateTextEmbeddings = uiStateTextEmbeddings.copy( 36 | state = State.Loading, 37 | similaritySentences = emptyList() 38 | ) 39 | viewModelScope.launch { 40 | mediaPipeEmbeddings.setUpMLModel(context) 41 | uiStateTextEmbeddings = uiStateTextEmbeddings.copy( 42 | sentences = listOf( 43 | "The next weekend will be your birthday", 44 | "Birds sing in the morning", 45 | "My cat sleeps a lot", 46 | "Pizza is my favorite food", 47 | "The sun is hot", 48 | "I like to play with my dog", 49 | "Flowers are colorful", 50 | "Books take us on adventures", 51 | "The moon comes out at night", 52 | "Ice cream melts quickly in the sun", 53 | "Soccer is a fun game", 54 | "My mom makes yummy cookies", 55 | "Cars go fast on the road", 56 | "I can count to ten", 57 | "Rainbows have many colors", 58 | "I love watching cartoons", 59 | ), 60 | state = State.Empty 61 | ) 62 | } 63 | 64 | } 65 | 66 | fun calculateSimilarity(mainSentence: String, sentences: List) { 67 | uiStateTextEmbeddings = uiStateTextEmbeddings.copy( 68 | state = State.Loading, 69 | similaritySentences = emptyList() 70 | ) 71 | 72 | viewModelScope.launch(Dispatchers.IO) { 73 | uiStateTextEmbeddings = try { 74 | val similarities = mediaPipeEmbeddings.getSimilarities(mainSentence, sentences) 75 | uiStateTextEmbeddings.copy( 76 | state = State.Success, 77 | similaritySentences = similarities 78 | ) 79 | } catch (e: Exception) { 80 | uiStateTextEmbeddings.copy( 81 | state = State.Error, 82 | errorMessage = e.message ?: "Error getting similarities" 83 | ) 84 | } 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/dogsembeddings/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/dogsembeddings/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | /* Other default colors to override 30 | background = Color(0xFFFFFBFE), 31 | surface = Color(0xFFFFFBFE), 32 | onPrimary = Color.White, 33 | onSecondary = Color.White, 34 | onTertiary = Color.White, 35 | onBackground = Color(0xFF1C1B1F), 36 | onSurface = Color(0xFF1C1B1F), 37 | */ 38 | ) 39 | 40 | @Composable 41 | fun DogsEmbeddingsTheme( 42 | darkTheme: Boolean = isSystemInDarkTheme(), 43 | // Dynamic color is available on Android 12+ 44 | dynamicColor: Boolean = true, 45 | content: @Composable () -> Unit 46 | ) { 47 | val colorScheme = when { 48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 49 | val context = LocalContext.current 50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 51 | } 52 | 53 | darkTheme -> DarkColorScheme 54 | else -> LightColorScheme 55 | } 56 | val view = LocalView.current 57 | if (!view.isInEditMode) { 58 | SideEffect { 59 | val window = (view.context as Activity).window 60 | window.statusBarColor = colorScheme.primary.toArgb() 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /app/src/main/java/ai/wordbox/dogsembeddings/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package ai.wordbox.dogsembeddings.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jggomez/AndroidMediaPipe/770ae2b35b8dc0d5575f4cc9c90100e101888ecd/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DogsEmbeddings 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |