kotlin DSL retrieving keys from other file - android

I am trying to switch my gradle files to Kotlin DSL. My project is making call
to an API.
In build.gradle(app) I had a function to retrieve an api key stored in an other
file keys.properties.
After some problem (for example) I rewrote the function to get the
key. I wrote the following function in build.gradle.kts:
import import java.io.File
fun readFileLineByLineUsingForEachLine2(fileName: String): HashMap<String, String>{
val items = HashMap<String, String>()
File(fileName).forEachLine {
items[it.split("=")[0]] = it.split("=")[1]
}
return items
}
Then I set a variable to hold the value of a particular key:
buildConfigField(String!, "API_KEY", returnMapOfKeys()["API_KEY"])
After fixing some errors I am stuck with the following one:
app/build.gradle.kts:49:36: Expecting ')'
which point on the line above with buildConfigField.
Does someone know where is this error ?
Or does someone know how to retrieve keys from files with Kotlin DSL ?

I solved my problem (it seems so.. check the edit!!). I ended up with the following function:
// Retrieve key for api
fun getApiKey(): String {
val items = HashMap<String, String>()
val f = File("keys.properties")
f.forEachLine {
items[it.split("=")[0]] = it.split("=")[1]
}
return items["API_KEY"]!!
}
And then I call buildConfigField as follow:
buildConfigField("String", "API_KEY", getApiKey())
There are no errors anymore on this part.
Edit
Once I have fixed all errors in the build.gradle.kts, the build of my project return that the file keys.properties could not be found: I had to fix my function getApiKey. Finally I could build and run my project with the following implementation:
// Return value of api key stored in `app/keys.properties`
fun getApiKey(): String {
val items = HashMap<String, String>()
val fl = rootProject.file("app/keys.properties")
(fl.exists())?.let {
fl.forEachLine {
items[it.split("=")[0]] = it.split("=")[1]
}
}
return items["API_KEY"]!!
}
This function is far to be good with all its hardcoded stuff but it allows to build my project.

Alternative
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
val apiKey = gradleLocalProperties(rootDir).getProperty("key.properties")
..
buildConfigField("String", "API_KEY", "\"$apiKey\"")

In your kts file, first import
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.util.Properties
then you can do this:
fun getApiKey(): String {
val fl = rootProject.file("gradle.properties") <- the path for the file where your key is.
if (fl.exists()) {
val properties = Properties()
properties.load(FileInputStream(fl))
return properties.getProperty("API_KEY") <- the name your give to it
} else {
throw FileNotFoundException()
}
}
Now you call use it: buildConfigField("String", "API_KEY", getApiKey())

Related

How to use interface in swift with Kotlin multiplatform moblie

I am trying to use interface in swift, but it unable to find the property
commainMain
interface ApplicationToken {
val accessToken: String
val refreshToken: String
}
iosMain
Platform.kt
lateinit var tokenProvider: ApplicationToken
HttpClient.kt
actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Darwin) {
config(this)
engine {
configureRequest {
setAllowsCellularAccess(true)
}
}
install(Auth) {
bearer {
loadTokens {
BearerTokens(tokenProvider.accessToken, "")
}
}
}
}
Now when I am to access tokenProvider in my swift code. It cannot find. I am adding image please have a look.
I tried another options to create class and implement my interface and call the class
class getToken : ApplicationToken {
let accessToken: String = ""
let refreshToken: String = ""
}
_ = getToken()
But it gives me the error. I cannot paste it because I don't understand in xcode.
When generating code for extensions (e.g. fun SomeClass.extension()) and for global variables, like in your case, Kotlin creates kind of a namespace, related to the filename.
In your case your property should be under PlatformKt.tokenProvider.
Usually when it's hard to find how your kotlin code is visible on iOS side, it's useful to check out framework header file. It should be places somewhere around here(replace shared with your framework name):
shared/build/cocoapods/framework/shared.framework/Headers/shared.h
I prefer adding this file by reference in my Xcode project, so I can have fast access all the time:
Then you can easily search through this file for your variable/class/etc name.

Switching to Kotlin DSL Unresolved reference when trying to access other file

I have an error when trying to use Kotlin DSL for my gradle files.
In build.gradle(app) I have a function to retrieve an api key stored in an
file keys.properties, the function in Groovy is the following:
// Retrieve key api
def getApiKey() {
def keysFile = file("keys.properties")
def keysProperties = new Properties()
keysProperties.load(new FileInputStream(keysFile))
def apiKey = keysProperties['API_KEY']
return apiKey
}
When switching to Kotlin DSL I naively changed the function as follow:
// Retrieve key for TMDB api
fun getApiKey() {
val keysFile = file("keys.properties")
val keysProperties = Properties()
keysProperties.load(FileInputStream(keysFile))
val apiKey = keysProperties["API_KEY"]
return apiKey
}
The build then returns the following error:
.../app/build.gradle.kts:13:26: Unresolved reference: Properties
Does anyone know how to fix that?
Edit
as suggested by #bam bam, adding an import import java.util.Properties solved the problems.. But other problems came, see this question
did you import class? add import java.util.Properties on top of your build.gradle.kts

How to define ANDROID_HOME before the project configuration phase in Android Gradle projects?

When building an Android application with Gradle we need to point the ANDROID_HOME to our build. Maybe using an environment variable or something in the local.properties file.
I'm trying to find a way to automatically define and use this, if possible.
I've almost achieved the expected result, but because I wasn't able to change the System.env environment variables, this vetoed me.
In this Android class com.android.build.gradle.internal.SdkHandler#findSdkLocation we can see how it is finding and configuring the android sdk location.
Do we have a way to set this environment variable before the project configuration phase starts?
It looks like it needs to be before the include(":android_project") in our settings.gradle.kts.
I've found a solution, not sure if is the best one.
Changing the System.getenv() variables, using a Kotlin adapted form of this answer:
#Suppress("UNCHECKED_CAST")
#Throws(Exception::class)
fun addAdditionalEnvironmentVariables(additionalEnvironmentVariables: Map<String, String>) {
try {
val processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment")
val theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment")
theEnvironmentField.isAccessible = true
val env = theEnvironmentField.get(null) as MutableMap<String, String>
env.putAll(additionalEnvironmentVariables)
val theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment")
theCaseInsensitiveEnvironmentField.isAccessible = true
val cienv = theCaseInsensitiveEnvironmentField.get(null) as MutableMap<String, String>
cienv.putAll(additionalEnvironmentVariables)
} catch (e: NoSuchFieldException) {
val classes = Collections::class.java.getDeclaredClasses()
val env = System.getenv()
for (cl in classes) {
if ("java.util.Collections\$UnmodifiableMap" == cl.getName()) {
val field = cl.getDeclaredField("m")
field.setAccessible(true)
val obj = field.get(env)
val map = obj as MutableMap<String, String>
map.clear()
map.putAll(additionalEnvironmentVariables)
}
}
}
}

How do I get Android string resources for flutter package?

I want to read the stings.xml file in another package.
I'm creating a flutter package
What should I do?
flutter example app string xml
<string name="client_id">clientid</string>
flutter package
context.getString(R.stirng.client_id); // not found
You can read it from Context after set it in xml file:
Add this class to your plugin:
import android.content.Context
import androidx.annotation.NonNull
class PluginUtilities
{
companion object {
#JvmStatic
fun getResourceFromContext(#NonNull context: Context, resName: String): String {
val stringRes = context.resources.getIdentifier(resName, "string", context.packageName)
if (stringRes == 0) {
throw IllegalArgumentException(String.format("The 'R.string.%s' value it's not defined in your project's resources file.", resName))
}
return context.getString(stringRes)
}
}
}
Then when you need any string from resources, you can call getResourceFromContext method, for example i will get access_token from resources:
val accessToken: String = PluginUtilities.getResourceFromContext(context,"access_token")
I hope the answer is useful...
You won't be able to access xml file in flutter package
You can create a file Strings.dart
class Strings {
static const String client_id = "xxxxxxxx";
}
And use it like this
Text(Strings.client_id, /*Code*/),
Don't forget to import Strings.dart
import '../Strings.dart';

Read CSV line-by-line in Kotlin

I'm writing a simple import application and need to read a CSV file, show result in a grid and show corrupted lines of the CSV file in another grid.
Is there any built-in lib for it or any easy pythonic-like way?
I'm doing it on android.
[Edit October 2019] A couple of months after I wrote this answer, Koyama Kenta wrote a Kotlin targeted library which can be found at https://github.com/doyaaaaaken/kotlin-csv and which looks much better to me than opencsv.
Example usage: (for more info see the github page mentioned)
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
fun main() {
csvReader().open("src/main/resources/test.csv") {
readAllAsSequence().forEach { row ->
//Do something
println(row) //[a, b, c]
}
}
}
For a complete minimal project with this example, see https://github.com/PHPirates/kotlin-csv-reader-example
Old answer using opencsv:
As suggested, it is convenient to use opencsv. Here is a somewhat minimal example:
// You can of course remove the .withCSVParser part if you use the default separator instead of ;
val csvReader = CSVReaderBuilder(FileReader("filename.csv"))
.withCSVParser(CSVParserBuilder().withSeparator(';').build())
.build()
// Maybe do something with the header if there is one
val header = csvReader.readNext()
// Read the rest
var line: Array<String>? = csvReader.readNext()
while (line != null) {
// Do something with the data
println(line[0])
line = csvReader.readNext()
}
As seen in the docs when you do not need to process every line separately you can get the result in the form of a Map:
import com.opencsv.CSVReaderHeaderAware
import java.io.FileReader
fun main() {
val reader = CSVReaderHeaderAware(FileReader("test.csv"))
val resultList = mutableListOf<Map<String, String>>()
var line = reader.readMap()
while (line != null) {
resultList.add(line)
line = reader.readMap()
}
println(resultList)
// Line 2, by column name
println(resultList[1]["my column name"])
}
Dependency for Gradle: compile 'com.opencsv:opencsv:4.6' or for Gradle Kotlin DSL: compile("com.opencsv:opencsv:4.6") (as always, check for latest version in docs).
In terms of easiness, kotlin written csv library is better.
For example, you can write code in DSL like way with below library that I created:
https://github.com/doyaaaaaken/kotlin-csv
csvReader().open("test.csv") {
readAllAsSequence().forEach { row ->
//Do something with the data
println(row)
}
}
Use opencsv.
This is gonna work like a charm for reading a CSV file.
As far as logging the corrupted lines is concerned you can do it using this logic.
while(input.hasNextLine())
{
try
{
//execute commands by reading them using input.nextLine()
}
catch (ex: UserDefinedException)
{
//catch/log the exceptions you're throwing
// log the corrupted line the continue to next iteration
}
}
Hope this helps.
I used net.sourceforge.javacsv with my Kotlin code for parsing CSV files. It is a "java" library but within kotlin it is fairly straightforward to work with it like
val reader = CsvReader("/path/to/file.csv").apply {
trimWhitespace = true
skipEmptyRecords = true
readHeaders()
}
while (reader.readRecord()) {
// do whatever
}
Frankly speaking, it is quite easy to make a simple reader in Kotlin using modern Java features, check this (REMEMBER to handle BOM :-)):
fun processLineByLine(csv: File, processor: (Map<String, String>) -> Unit) {
val BOM = "\uFEFF"
val header = csv.useLines { it.firstOrNull()?.replace(BOM, "")?.split(",") }
?: throw Exception("This file does not contain a valid header")
csv.useLines { linesSequence ->
linesSequence
.drop(1)
.map { it.split(",") }
.map { header.zip(it).toMap() }
.forEach(processor)
}
}
Than you can use it as follows (depends on your file structure):
processLineByLine(File("./my-file.csv")) { row ->
println("UserId: ${row["userId"]}")
println("Email: ${row["email"]}")
}
If you prefer to use your own data class for each row you should have a look at my solution https://github.com/gmuth/ipp-client-kotlin/blob/master/src/main/kotlin/de/gmuth/csv/CSVTable.kt
data class User(
val name: String,
val phone: String,
val email: String
) {
constructor(columns: List<String>) : this(
name = columns[0],
phone = columns[1],
email = columns[2]
)
}
CSVTable.print(FileInputStream("users.csv"))
val userList = CSVTable(FileInputStream("users.csv"), ::User).rows
I know i'm a bit late, but I recently had problems with parsing CSV and there seemed to be no library good enough for what I was looking for, so I created my own called Kotlin CSV stream.
This library is special because it doesn't throw exceptions on an invalid input, but returns in the result instead, which might be useful in some cases.
Here is an example of how easy it is to use
val reader = CsvReader()
.readerForType<CsvPerson>()
val people = reader.read(csv).map { it.getResultOrThrow() }.toList()
For version of commons-csv version 1.9.0, have implemented below code to get results.
It uses CSVBuilder and CSVFormat to get records with skip headers and auto-identify headers on basis of first row.
fun csvReader(file: MultipartFile): ResultListObject? {
var result = ResultListObject()
var csvFormat=CSVFormat.Builder.create().setHeader().setSkipHeaderRecord(true).build()
var csvRecords = CSVParser(file.inputStream.bufferedReader(), csvFormat)
csvRecords.forEach{csvRecords->
rowRecord.field1=records.get("field1")
rowRecord.field2=records.get("field2")
...
...
result.add(rowRecord)
}
return result
}

Categories

Resources