├── .DS_Store ├── Android-Studio ├── Electric-Eel │ └── README.md ├── LayoutInspector │ └── README.md └── Useful-Plugins │ └── README.md ├── Animation ├── Material Motion.pdf ├── MaterialMotion.zip └── README.md ├── AnimationCompose └── jetpack_compose_anim_basics.pdf ├── CI-CD └── Bitrise │ └── README.md ├── CameraX └── README.md ├── CanvasAPI ├── Android - Canvas.pdf └── README.md ├── DynamicIcon └── Dinamik Ikon Degisikligi.pdf ├── EdgeToEdge └── edge_to_edge_present.pdf ├── LICENSE ├── NFC ├── CrystalBall │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── .name │ │ ├── compiler.xml │ │ ├── deploymentTargetDropDown.xml │ │ ├── git_toolbox_prj.xml │ │ ├── gradle.xml │ │ ├── inspectionProfiles │ │ │ └── Project_Default.xml │ │ ├── kotlinc.xml │ │ ├── ktfmt.xml │ │ ├── ktlint-plugin.xml │ │ ├── migrations.xml │ │ ├── misc.xml │ │ └── vcs.xml │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle.kts │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── moter │ │ │ │ └── crystalball │ │ │ │ └── ExampleInstrumentedTest.kt │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── moter │ │ │ │ │ └── crystalball │ │ │ │ │ ├── App.kt │ │ │ │ │ ├── GeminiChatProvider.kt │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── MainViewModel.kt │ │ │ │ │ ├── ScreenContent.kt │ │ │ │ │ ├── ui │ │ │ │ │ └── theme │ │ │ │ │ │ ├── Color.kt │ │ │ │ │ │ ├── Theme.kt │ │ │ │ │ │ └── Type.kt │ │ │ │ │ └── utl │ │ │ │ │ ├── DefaultStateDelegate.kt │ │ │ │ │ ├── PromptHelper.kt │ │ │ │ │ └── StateDelegate.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ ├── background.jpg │ │ │ │ ├── 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 │ │ │ │ ├── raw │ │ │ │ └── video.mp4 │ │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ │ └── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ ├── data_extraction_rules.xml │ │ │ │ └── nfc_tech_filter.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── moter │ │ │ └── crystalball │ │ │ └── ExampleUnitTest.kt │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ │ ├── libs.versions.toml │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle.kts ├── NFC_overview.pdf └── NfcWriter │ ├── .gitignore │ ├── .idea │ ├── .gitignore │ ├── .name │ ├── compiler.xml │ ├── deploymentTargetDropDown.xml │ ├── gradle.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── kotlinc.xml │ ├── ktfmt.xml │ ├── migrations.xml │ └── misc.xml │ ├── app │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── moter │ │ │ └── nfcwriter │ │ │ └── ExampleInstrumentedTest.kt │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── moter │ │ │ │ └── nfcwriter │ │ │ │ ├── MainActivity.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 │ │ │ └── nfc_tech_filter.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── moter │ │ └── nfcwriter │ │ └── ExampleUnitTest.kt │ ├── build.gradle.kts │ ├── gradle.properties │ ├── gradle │ ├── libs.versions.toml │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle.kts ├── Network ├── Interceptors │ └── README.md └── ProxyMan │ └── README.md ├── Others └── Slack │ └── Workflows │ └── README.md └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/.DS_Store -------------------------------------------------------------------------------- /Android-Studio/Electric-Eel/README.md: -------------------------------------------------------------------------------- 1 | #TODO 2 | -------------------------------------------------------------------------------- /Android-Studio/LayoutInspector/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Android-Studio/Useful-Plugins/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Animation/Material Motion.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/Animation/Material Motion.pdf -------------------------------------------------------------------------------- /Animation/MaterialMotion.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/Animation/MaterialMotion.zip -------------------------------------------------------------------------------- /Animation/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /AnimationCompose/jetpack_compose_anim_basics.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/AnimationCompose/jetpack_compose_anim_basics.pdf -------------------------------------------------------------------------------- /CI-CD/Bitrise/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CameraX/README.md: -------------------------------------------------------------------------------- 1 | Project Codes 2 | 3 | MainActivity -> https://gist.github.com/TamerSarioglu/76a3e8e659669cd2bf1e19b0f3c8f965 4 | 5 | activity_main.xml -> https://gist.github.com/TamerSarioglu/a3cddb87284ca01335711d33742e3ea1 6 | 7 | AndroidManifest.xml -> https://gist.github.com/TamerSarioglu/ab121d68e943d1e4c5167eb6949edbec 8 | 9 | build.gradle:(Module:app) -> https://gist.github.com/TamerSarioglu/f62ae16d63b6444bbf76cdeff690ed84 10 | 11 | build.gradle:(Project:CameraX) -> https://gist.github.com/TamerSarioglu/5d95563e89dff30cf05ca8d1665e316e 12 | -------------------------------------------------------------------------------- /CanvasAPI/Android - Canvas.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/CanvasAPI/Android - Canvas.pdf -------------------------------------------------------------------------------- /CanvasAPI/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /DynamicIcon/Dinamik Ikon Degisikligi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/DynamicIcon/Dinamik Ikon Degisikligi.pdf -------------------------------------------------------------------------------- /EdgeToEdge/edge_to_edge_present.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/EdgeToEdge/edge_to_edge_present.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mobillium 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/.name: -------------------------------------------------------------------------------- 1 | Crystal Ball -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/ktfmt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/ktlint-plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DISABLED 5 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /NFC/CrystalBall/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /NFC/CrystalBall/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | 3 | plugins { 4 | alias(libs.plugins.androidApplication) 5 | alias(libs.plugins.jetbrainsKotlinAndroid) 6 | alias(libs.plugins.kotlinKapt) 7 | alias(libs.plugins.hilt) 8 | 9 | } 10 | 11 | android { 12 | namespace = "com.moter.crystalball" 13 | compileSdk = 34 14 | 15 | defaultConfig { 16 | applicationId = "com.moter.crystalball" 17 | minSdk = 26 18 | targetSdk = 34 19 | versionCode = 1 20 | versionName = "1.0" 21 | 22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 23 | vectorDrawables { 24 | useSupportLibrary = true 25 | } 26 | val properties = Properties() 27 | properties.load(project.rootProject.file("local.properties").inputStream()) 28 | buildConfigField("String", "API_KEY", "\"${properties.getProperty("geminiApiKey")}\"") 29 | 30 | 31 | } 32 | 33 | buildTypes { 34 | android.buildFeatures.buildConfig = true 35 | release { 36 | isMinifyEnabled = false 37 | proguardFiles( 38 | getDefaultProguardFile("proguard-android-optimize.txt"), 39 | "proguard-rules.pro" 40 | ) 41 | } 42 | } 43 | compileOptions { 44 | sourceCompatibility = JavaVersion.VERSION_1_8 45 | targetCompatibility = JavaVersion.VERSION_1_8 46 | } 47 | kotlinOptions { 48 | jvmTarget = "1.8" 49 | } 50 | buildFeatures { 51 | compose = true 52 | } 53 | composeOptions { 54 | kotlinCompilerExtensionVersion = "1.5.1" 55 | } 56 | packaging { 57 | resources { 58 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 59 | } 60 | } 61 | } 62 | 63 | dependencies { 64 | 65 | implementation(libs.androidx.core.ktx) 66 | implementation(libs.androidx.lifecycle.runtime.ktx) 67 | implementation(libs.androidx.activity.compose) 68 | implementation(platform(libs.androidx.compose.bom)) 69 | implementation(libs.androidx.ui) 70 | implementation(libs.androidx.ui.graphics) 71 | implementation(libs.androidx.ui.tooling.preview) 72 | implementation(libs.androidx.material3) 73 | 74 | // Gemini 75 | implementation(libs.generativeai) 76 | 77 | // Hilt 78 | implementation(libs.hilt.android) 79 | implementation(libs.androidx.hilt.navigation.compose) 80 | kapt(libs.hilt.android.compiler) 81 | 82 | // ExoPlayer 83 | implementation(libs.exoplayer) 84 | 85 | 86 | testImplementation(libs.junit) 87 | androidTestImplementation(libs.androidx.junit) 88 | androidTestImplementation(libs.androidx.espresso.core) 89 | androidTestImplementation(platform(libs.androidx.compose.bom)) 90 | androidTestImplementation(libs.androidx.ui.test.junit4) 91 | debugImplementation(libs.androidx.ui.tooling) 92 | debugImplementation(libs.androidx.ui.test.manifest) 93 | } 94 | 95 | // Allow references to generated code 96 | kapt { 97 | correctErrorTypes = true 98 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/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 -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/androidTest/java/com/moter/crystalball/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball 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("com.moter.crystalball", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/App.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class App : Application() { 8 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/GeminiChatProvider.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball 2 | 3 | import com.google.ai.client.generativeai.GenerativeModel 4 | import com.google.ai.client.generativeai.type.BlockThreshold 5 | import com.google.ai.client.generativeai.type.HarmCategory 6 | import com.google.ai.client.generativeai.type.SafetySetting 7 | import com.google.ai.client.generativeai.type.generationConfig 8 | 9 | object GeminiChatProvider { 10 | private val safetySettings = listOf( 11 | SafetySetting(HarmCategory.SEXUALLY_EXPLICIT, BlockThreshold.NONE), 12 | SafetySetting(HarmCategory.HATE_SPEECH, BlockThreshold.NONE), 13 | SafetySetting(HarmCategory.DANGEROUS_CONTENT, BlockThreshold.NONE), 14 | SafetySetting(HarmCategory.HARASSMENT, BlockThreshold.NONE), 15 | ) 16 | 17 | private val config = generationConfig { 18 | stopSequences = listOf() // restricted words 19 | } 20 | 21 | 22 | val generativeModel = GenerativeModel( 23 | modelName = "gemini-pro", 24 | apiKey = BuildConfig.API_KEY, 25 | generationConfig = config, 26 | safetySettings = safetySettings, 27 | ) 28 | 29 | 30 | 31 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball 2 | 3 | import android.app.PendingIntent 4 | import android.content.Intent 5 | import android.content.IntentFilter 6 | import android.graphics.Color 7 | import android.nfc.NdefMessage 8 | import android.nfc.NfcAdapter 9 | import android.nfc.Tag 10 | import android.os.Build 11 | import android.os.Bundle 12 | import android.util.Log 13 | import android.widget.Toast 14 | import androidx.activity.ComponentActivity 15 | import androidx.activity.SystemBarStyle 16 | import androidx.activity.compose.setContent 17 | import androidx.activity.enableEdgeToEdge 18 | import androidx.activity.viewModels 19 | import androidx.compose.foundation.layout.fillMaxSize 20 | import androidx.compose.material3.MaterialTheme 21 | import androidx.compose.material3.Surface 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.ui.Modifier 24 | import androidx.compose.ui.tooling.preview.Preview 25 | import com.moter.crystalball.ui.theme.CrystalBallTheme 26 | import dagger.hilt.android.AndroidEntryPoint 27 | import java.io.UnsupportedEncodingException 28 | import java.nio.charset.Charset 29 | import kotlin.experimental.and 30 | 31 | @AndroidEntryPoint 32 | class MainActivity : ComponentActivity() { 33 | private lateinit var intentFiltersArray: Array 34 | private var nfcAdapter: NfcAdapter? = null 35 | private var pendingIntent: PendingIntent? = null 36 | private val viewModel: MainViewModel by viewModels() 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | enableEdgeToEdge( 39 | navigationBarStyle = SystemBarStyle.dark(Color.TRANSPARENT), 40 | statusBarStyle = SystemBarStyle.dark(Color.TRANSPARENT) 41 | ) 42 | super.onCreate(savedInstanceState) 43 | initNfcConfigurations() 44 | intent?.handleDiscoveredTag() 45 | setContent { 46 | CrystalBallTheme { 47 | Surface( 48 | modifier = Modifier.fillMaxSize(), 49 | color = MaterialTheme.colorScheme.background 50 | ) { 51 | ScreenContent() 52 | } 53 | } 54 | } 55 | } 56 | 57 | private fun initNfcConfigurations() { 58 | nfcAdapter = NfcAdapter.getDefaultAdapter(this) 59 | if (nfcAdapter == null) { 60 | finish() 61 | } 62 | 63 | val intent = Intent(this, javaClass).apply { 64 | addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) 65 | } 66 | pendingIntent = PendingIntent.getActivity( 67 | this, 0, intent, 68 | PendingIntent.FLAG_MUTABLE 69 | ) 70 | 71 | intentFiltersArray = arrayOf( 72 | IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED), 73 | IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED), 74 | IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED) 75 | ) 76 | } 77 | 78 | public override fun onNewIntent(intent: Intent) { 79 | super.onNewIntent(intent) 80 | intent.handleDiscoveredTag() 81 | } 82 | 83 | public override fun onPause() { 84 | super.onPause() 85 | nfcAdapter?.disableForegroundDispatch(this) 86 | } 87 | 88 | public override fun onResume() { 89 | super.onResume() 90 | nfcAdapter?.enableForegroundDispatch( 91 | this, 92 | pendingIntent, 93 | intentFiltersArray, 94 | null, 95 | ) 96 | } 97 | 98 | private fun Intent.handleDiscoveredTag() { 99 | println("Scan handleDiscoveredTag ${this.action}") 100 | if (NfcAdapter.ACTION_TAG_DISCOVERED == this.action || NfcAdapter.ACTION_NDEF_DISCOVERED == this.action) { 101 | val tag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 102 | this.getParcelableExtra(NfcAdapter.EXTRA_TAG, Tag::class.java) 103 | } else { 104 | this.getParcelableExtra(NfcAdapter.EXTRA_TAG) 105 | } 106 | 107 | tag?.id?.let { 108 | println("Scan tagId $it") 109 | val tagValue = it.toHexString() 110 | viewModel.handleEvent(MainScreenEvent.NfcScanned(tagValue)) 111 | Toast.makeText(this@MainActivity, "NFC tag detected: $tagValue", Toast.LENGTH_SHORT) 112 | .show() 113 | } 114 | 115 | parseNDefMessage(this) 116 | } 117 | } 118 | 119 | 120 | private fun parseNDefMessage(intent: Intent) { 121 | val parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES) 122 | val ndefMessages = mutableListOf() 123 | if (parcelables != null) { 124 | for (i in parcelables.indices) { 125 | ndefMessages.add(i, parcelables[i] as NdefMessage) 126 | } 127 | buildTagViews(ndefMessages.toTypedArray()) 128 | } 129 | } 130 | 131 | private fun buildTagViews(ndefMessages: Array) { 132 | if (ndefMessages.isEmpty()) return 133 | var text = "" 134 | val payload = ndefMessages[0].records[0].payload 135 | val textEncoding: Charset = 136 | if ((payload[0] and 128.toByte()).toInt() == 0) Charsets.UTF_8 else Charsets.UTF_16 // Get the Text Encoding 137 | val languageCodeLength: Int = 138 | (payload[0] and 51).toInt() // Get the Language Code, e.g. "en" 139 | try { 140 | // Get the Text 141 | text = String( 142 | payload, 143 | languageCodeLength + 1, 144 | payload.size - languageCodeLength - 1, 145 | textEncoding 146 | ) 147 | } catch (e: UnsupportedEncodingException) { 148 | Log.e("UnsupportedEncoding", e.toString()) 149 | } 150 | Toast.makeText(this@MainActivity, "NFC message: $text", Toast.LENGTH_SHORT) 151 | .show() 152 | } 153 | 154 | 155 | private fun ByteArray.toHexString(): String { 156 | val hexChars = "0123456789ABCDEF" 157 | val result = StringBuilder(size * 2) 158 | 159 | map { byte -> 160 | val value = byte.toInt() 161 | val hexChar1 = hexChars[value shr 4 and 0x0F] 162 | val hexChar2 = hexChars[value and 0x0F] 163 | result.append(hexChar1) 164 | result.append(hexChar2) 165 | } 166 | 167 | return result.toString() 168 | } 169 | 170 | 171 | } 172 | 173 | 174 | @Preview(showBackground = true) 175 | @Composable 176 | fun GreetingPreview() { 177 | CrystalBallTheme { 178 | ScreenContent() 179 | } 180 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import com.moter.crystalball.utl.DefaultStateDelegate 6 | import com.moter.crystalball.utl.PromptHelper 7 | import com.moter.crystalball.utl.StateDelegate 8 | import kotlinx.coroutines.launch 9 | 10 | class MainViewModel : ViewModel(), 11 | StateDelegate by DefaultStateDelegate(initialState = MainScreenState()) { 12 | 13 | private fun generateFortuneTelling(prompt: String) { 14 | viewModelScope.launch { 15 | val response = GeminiChatProvider.generativeModel.generateContent(prompt) 16 | response.text?.let { 17 | updateState { 18 | copy( 19 | response = it, 20 | isPlaying = false, 21 | isDisplayingResult = true 22 | ) 23 | } 24 | } 25 | } 26 | } 27 | 28 | fun handleEvent(event: MainScreenEvent) { 29 | when (event) { 30 | is MainScreenEvent.FortuneTelling -> generateFortuneTelling(event.prompt) 31 | is MainScreenEvent.NfcScanned -> handleNfcScanned(event.tagValue) 32 | is MainScreenEvent.DismissResult -> handleDismissResult() 33 | } 34 | } 35 | 36 | private fun handleDismissResult() { 37 | updateState { 38 | copy(isDisplayingResult = false) 39 | } 40 | } 41 | 42 | private fun handleNfcScanned(tagValue: String) { 43 | val prompt = PromptHelper.generatePrompt(tagValue) 44 | handleEvent(MainScreenEvent.FortuneTelling(prompt.second)) 45 | updateState { 46 | copy( 47 | isPlaying = true, 48 | nfcValue = tagValue, 49 | emotionalStates = prompt.first 50 | ) 51 | } 52 | } 53 | } 54 | 55 | 56 | data class MainScreenState( 57 | val isLoading: Boolean = false, 58 | val isError: Boolean = false, 59 | val isPlaying: Boolean = false, 60 | val isDisplayingResult: Boolean = false, 61 | val nfcValue: String = "", 62 | val emotionalStates: String = "", 63 | val response: String = "", 64 | ) 65 | 66 | 67 | sealed interface MainScreenEvent { 68 | data class FortuneTelling(val prompt: String) : MainScreenEvent 69 | 70 | data class NfcScanned(val tagValue: String) : MainScreenEvent 71 | 72 | object DismissResult : MainScreenEvent 73 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/ScreenContent.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxSize 8 | import androidx.compose.foundation.layout.height 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.rememberScrollState 11 | import androidx.compose.foundation.verticalScroll 12 | import androidx.compose.material3.ExperimentalMaterial3Api 13 | import androidx.compose.material3.ModalBottomSheet 14 | import androidx.compose.material3.Text 15 | import androidx.compose.material3.rememberModalBottomSheetState 16 | import androidx.compose.runtime.Composable 17 | import androidx.compose.runtime.DisposableEffect 18 | import androidx.compose.runtime.LaunchedEffect 19 | import androidx.compose.runtime.collectAsState 20 | import androidx.compose.runtime.getValue 21 | import androidx.compose.runtime.mutableStateOf 22 | import androidx.compose.runtime.remember 23 | import androidx.compose.runtime.setValue 24 | import androidx.compose.ui.Modifier 25 | import androidx.compose.ui.graphics.Color 26 | import androidx.compose.ui.platform.LocalContext 27 | import androidx.compose.ui.platform.LocalLifecycleOwner 28 | import androidx.compose.ui.text.font.FontStyle 29 | import androidx.compose.ui.tooling.preview.Preview 30 | import androidx.compose.ui.unit.dp 31 | import androidx.compose.ui.unit.sp 32 | import androidx.compose.ui.viewinterop.AndroidView 33 | import androidx.hilt.navigation.compose.hiltViewModel 34 | import androidx.lifecycle.Lifecycle 35 | import androidx.lifecycle.LifecycleEventObserver 36 | import com.google.android.exoplayer2.ExoPlayer 37 | import com.google.android.exoplayer2.MediaItem 38 | import com.google.android.exoplayer2.ui.PlayerView 39 | 40 | @Composable 41 | fun ScreenContent(modifier: Modifier = Modifier) { 42 | val viewModel = hiltViewModel() 43 | val state by viewModel.stateFlow.collectAsState() 44 | 45 | Box( 46 | modifier = modifier 47 | .fillMaxSize() 48 | .background(Color.Black) 49 | ) { 50 | VideoPlayer(state.isPlaying) 51 | if (state.isDisplayingResult) { 52 | ResultBottomSheet( 53 | response = state.response, 54 | emotionalStates = state.emotionalStates, 55 | onDismissRequest = { viewModel.handleEvent(MainScreenEvent.DismissResult) }) 56 | } 57 | } 58 | } 59 | 60 | @Composable 61 | fun VideoPlayer(isPlaying: Boolean) { 62 | var lifecycle by remember { 63 | mutableStateOf(Lifecycle.Event.ON_CREATE) 64 | } 65 | 66 | val context = LocalContext.current 67 | 68 | val mediaItem = MediaItem.fromUri("android.resource://${context.packageName}/${R.raw.video}") 69 | val exoPlayer = remember(context, mediaItem) { 70 | ExoPlayer.Builder(context) 71 | .build() 72 | .apply { 73 | setMediaItem(mediaItem) 74 | repeatMode = ExoPlayer.REPEAT_MODE_ALL 75 | playWhenReady = false 76 | prepare() 77 | } 78 | } 79 | 80 | LaunchedEffect(isPlaying) { 81 | if (isPlaying) { 82 | exoPlayer.play() 83 | } else { 84 | exoPlayer.pause() 85 | } 86 | } 87 | 88 | 89 | val lifecycleOwner = LocalLifecycleOwner.current 90 | DisposableEffect(key1 = lifecycleOwner) { 91 | val observer = LifecycleEventObserver { _, event -> 92 | lifecycle = event 93 | } 94 | lifecycleOwner.lifecycle.addObserver(observer) 95 | 96 | onDispose { 97 | exoPlayer.release() 98 | lifecycleOwner.lifecycle.removeObserver(observer) 99 | } 100 | } 101 | Box { 102 | AndroidView( 103 | factory = { 104 | PlayerView(context).apply { 105 | player = exoPlayer 106 | useController = false 107 | } 108 | }, 109 | modifier = Modifier.fillMaxSize(), 110 | update = { 111 | when (lifecycle) { 112 | Lifecycle.Event.ON_RESUME -> { 113 | it.onPause() 114 | it.player?.pause() 115 | } 116 | 117 | Lifecycle.Event.ON_PAUSE -> { 118 | it.onResume() 119 | } 120 | 121 | else -> Unit 122 | } 123 | } 124 | ) 125 | } 126 | } 127 | 128 | @OptIn(ExperimentalMaterial3Api::class) 129 | @Composable 130 | fun ResultBottomSheet(response: String, emotionalStates: String, onDismissRequest: () -> Unit) { 131 | val sheetState = rememberModalBottomSheetState() 132 | ModalBottomSheet( 133 | sheetState = sheetState, 134 | onDismissRequest = onDismissRequest 135 | ) { 136 | Column( 137 | modifier = Modifier 138 | .padding(20.dp) 139 | .verticalScroll(rememberScrollState()) 140 | ) { 141 | Text(text = response) 142 | Spacer(modifier = Modifier.height(50.dp)) 143 | Text(text = emotionalStates, fontSize = 12.sp, fontStyle = FontStyle.Italic) 144 | } 145 | } 146 | } 147 | 148 | @Preview 149 | @Composable 150 | fun ScreenContentPreview() { 151 | ScreenContent() 152 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball.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) -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball.ui.theme 2 | 3 | import android.app.Activity 4 | import android.graphics.Color 5 | import android.os.Build 6 | import androidx.compose.foundation.isSystemInDarkTheme 7 | import androidx.compose.material3.MaterialTheme 8 | import androidx.compose.material3.darkColorScheme 9 | import androidx.compose.material3.dynamicDarkColorScheme 10 | import androidx.compose.material3.dynamicLightColorScheme 11 | import androidx.compose.material3.lightColorScheme 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.SideEffect 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 CrystalBallTheme( 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 = Color.TRANSPARENT 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball.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 | ) -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/utl/DefaultStateDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball.utl 2 | 3 | import kotlinx.coroutines.flow.MutableStateFlow 4 | import kotlinx.coroutines.flow.StateFlow 5 | import kotlinx.coroutines.flow.asStateFlow 6 | import kotlinx.coroutines.flow.update 7 | 8 | class DefaultStateDelegate(initialState: State) : StateDelegate { 9 | private val _stateFlow = MutableStateFlow(initialState) 10 | override val stateFlow: StateFlow = _stateFlow.asStateFlow() 11 | 12 | override val state: State 13 | get() = stateFlow.value 14 | 15 | override fun updateState(reduce: State.() -> State) { 16 | _stateFlow.update(reduce) 17 | } 18 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/utl/PromptHelper.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball.utl 2 | 3 | import java.time.LocalDate 4 | import kotlin.random.Random 5 | 6 | object PromptHelper { 7 | 8 | val emotionalStates = listOf( 9 | "Mutlu", 10 | "Mutsuz", 11 | "Depresif", 12 | "Heyecanlı", 13 | "Sakin", 14 | "Huzurlu", 15 | "Öfkeli", 16 | "Korkmuş", 17 | "Şaşırmış", 18 | "Utangaç", 19 | "Gururlu", 20 | "Aşk dolu", 21 | "Nefret dolu", 22 | "Kıskanç", 23 | "Hayal kırıklığına uğramış", 24 | "Umutlu", 25 | "Umutsuz", 26 | "Endişeli", 27 | "Tedirgin", 28 | "Rahat", 29 | "Güvende", 30 | "Güvensiz", 31 | "Düşünceli", 32 | "Yaratıcı", 33 | "Odaklı", 34 | "Dağınık", 35 | "Enerjik", 36 | "Yorgun", 37 | "Motivasyonlu", 38 | "Tembel", 39 | "Kararsız", 40 | "Cesaretli", 41 | "Korkak", 42 | "Yalnız", 43 | "Eşlik eden", 44 | "Sevilen", 45 | "Reddedilen", 46 | "Arkadaş canlısı", 47 | "Sosyal", 48 | "İçe dönük", 49 | "Dışa dönük", 50 | "Lider", 51 | "Takipçi", 52 | "Ağrılı", 53 | "Rahat", 54 | "Hasta", 55 | "Sağlıklı", 56 | "Aç", 57 | "Tok", 58 | "Uykusuz", 59 | "Dinlenmiş", 60 | "Aydınlanmış", 61 | "Kafası karışık", 62 | "Spiritüel", 63 | "Materyalist", 64 | "Optimist", 65 | "Pesimist", 66 | "Minnettar", 67 | "Kindar", 68 | "Doğum günü", 69 | "Yıldönümü", 70 | "Tatil", 71 | "Cenaze", 72 | "Mezuniyet", 73 | "Düğün", 74 | "İş görüşmesi", 75 | "Sınav", 76 | ) 77 | 78 | 79 | private fun generateIndices(text: String, list: List): List { 80 | val indices = mutableListOf() 81 | val random = Random(text.hashCode() + LocalDate.now().hashCode()) 82 | 83 | while (indices.size < 5) { 84 | val index = random.nextInt(list.size) 85 | if (index !in indices) { 86 | indices.add(index) 87 | } 88 | } 89 | 90 | return indices 91 | } 92 | 93 | fun generatePrompt(text: String): Pair { 94 | val indices = generateIndices(text = text, list = emotionalStates) 95 | val emotionalList = mutableListOf() 96 | indices.forEach { 97 | emotionalList.add(emotionalStates[it]) 98 | } 99 | 100 | return Pair(emotionalList.joinToString(),getPromptTemplate(emotionalList)) 101 | } 102 | 103 | private fun getPromptTemplate(emotionalStates: List): String { 104 | return "Fal yorumu yapan bir uygulama için vereceğim duygu durumlarına göre fal yorumu üretmeni istiyorum. " + 105 | "Bu yorum şu başlıklar altında olmalı; Genel Yorum, Kişisel Yorum, Tavsiye ve Önerilen Eylemler. " + 106 | "Her duygu durumu için ayrı ayrı yorum istemiyorum. Hepsinin ortak yorumlandığı fal yorumu yeterli olur." + 107 | "Hadi başlayalım; ${emotionalStates.joinToString()}" 108 | } 109 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/java/com/moter/crystalball/utl/StateDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.moter.crystalball.utl 2 | 3 | import kotlinx.coroutines.flow.StateFlow 4 | 5 | interface StateDelegate { 6 | 7 | val stateFlow: StateFlow 8 | 9 | val state: State 10 | 11 | fun updateState(reduce: State.() -> State) 12 | } -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/drawable/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/drawable/background.jpg -------------------------------------------------------------------------------- /NFC/CrystalBall/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 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/raw/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mobillium/tech-talks-android/9e86bf75f1de7970e2a3cc05da82b94470a0f4ae/NFC/CrystalBall/app/src/main/res/raw/video.mp4 -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Crystal Ball 3 | -------------------------------------------------------------------------------- /NFC/CrystalBall/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |