Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose UI #296

Merged
merged 11 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
java: [1.8, 1.11]
java: ['8', '11']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2

- uses: actions/setup-java@v1
- uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
cache: 'gradle'

- name: Build Library
run: ./gradlew build
Expand All @@ -25,5 +27,6 @@ jobs:
run: ./gradlew publishToMavenLocal

- name: Build Examples
if: matrix.java == '11'
run: ./gradlew build
working-directory: ./examples
4 changes: 3 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ The example sources are organized into the following top-level folders:
- <details>
<summary>Android example</summary>

*NOTE: You must use JDK 11 or higher*

Start the server:

```sh
Expand All @@ -82,7 +84,7 @@ The example sources are organized into the following top-level folders:
mkdir cmdline-tools
mv latest cmdline-tools
cmdline-tools/latest/bin/sdkmanager --update
cmdline-tools/latest/bin/sdkmanager "platforms;android-30" "build-tools;30.0.2" "extras;google;m2repository" "extras;android;m2repository"
cmdline-tools/latest/bin/sdkmanager "platforms;android-31" "build-tools;31.0.0" "extras;google;m2repository" "extras;android;m2repository"
cmdline-tools/latest/bin/sdkmanager --licenses
```

Expand Down
38 changes: 32 additions & 6 deletions examples/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,32 @@ plugins {
kotlin("android")
}

val composeVersion = "1.0.5"

dependencies {
implementation(project(":stub-android"))
implementation("androidx.appcompat:appcompat:1.3.1")
implementation(kotlin("stdlib"))
implementation("androidx.activity:activity-compose:1.4.0")
implementation("androidx.appcompat:appcompat:1.4.0")

implementation("androidx.compose.foundation:foundation-layout:$composeVersion")
implementation("androidx.compose.material:material:$composeVersion")
implementation("androidx.compose.runtime:runtime:$composeVersion")
implementation("androidx.compose.ui:ui:$composeVersion")

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:${rootProject.ext["coroutinesVersion"]}")

runtimeOnly("io.grpc:grpc-okhttp:${rootProject.ext["grpcVersion"]}")
}

android {
compileSdkVersion(30)
buildToolsVersion = "30.0.2"
compileSdkVersion(31)
buildToolsVersion = "31.0.0"

defaultConfig {
applicationId = "io.grpc.examples.hello"
minSdkVersion(26)
targetSdkVersion(30)
targetSdkVersion(31)
versionCode = 1
versionName = "1.0"

Expand All @@ -31,7 +43,21 @@ android {
sourceSets["main"].java.srcDir("src/main/kotlin")

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

buildFeatures {
compose = true
}

composeOptions {
kotlinCompilerExtensionVersion = composeVersion
}
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
}
1 change: 0 additions & 1 deletion examples/android/gradle.properties

This file was deleted.

13 changes: 7 additions & 6 deletions examples/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@android:drawable/btn_star"
android:label="@string/app_label">
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@android:drawable/btn_star"
android:label="@string/app_label">
<activity
android:theme="@style/Theme.AppCompat.NoActionBar"
android:name="io.grpc.examples.helloworld.MainActivity">
android:theme="@style/Theme.AppCompat.NoActionBar"
android:name="io.grpc.examples.helloworld.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,83 +1,107 @@
package io.grpc.examples.helloworld

import android.net.Uri
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.KeyEvent
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import io.grpc.ManagedChannel
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import io.grpc.ManagedChannelBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.runBlocking
import java.net.URL
import java.util.logging.Logger
import kotlinx.coroutines.launch
import java.io.Closeable

// todo: suspend funs
class MainActivity : AppCompatActivity() {
private val logger = Logger.getLogger(this.javaClass.name)

private fun channel(): ManagedChannel {
val url = URL(resources.getString(R.string.server_url))
val port = if (url.port == -1) url.defaultPort else url.port
private val uri by lazy { Uri.parse(resources.getString(R.string.server_url)) }
private val greeterService by lazy { GreeterRCP(uri) }

logger.info("Connecting to ${url.host}:$port")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val builder = ManagedChannelBuilder.forAddress(url.host, port)
if (url.protocol == "https") {
setContent {
Surface(color = MaterialTheme.colors.background) {
Greeter(greeterService)
}
}
}

override fun onDestroy() {
super.onDestroy()
greeterService.close()
}
}

class GreeterRCP(uri: Uri) : Closeable {
val responseState = mutableStateOf("")

private val channel = let {
println("Connecting to ${uri.host}:${uri.port}")

val builder = ManagedChannelBuilder.forAddress(uri.host, uri.port)
if (uri.scheme == "https") {
builder.useTransportSecurity()
} else {
builder.usePlaintext()
}

return builder.executor(Dispatchers.Default.asExecutor()).build()
builder.executor(Dispatchers.IO.asExecutor()).build()
}

// lazy otherwise resources is null
private val greeter by lazy { GreeterGrpcKt.GreeterCoroutineStub(channel()) }
private val greeter = GreeterGrpcKt.GreeterCoroutineStub(channel)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
suspend fun sayHello(name: String) {
try {
val request = helloRequest { this.name = name }
val response = greeter.sayHello(request)
responseState.value = response.message
} catch (e: Exception) {
responseState.value = e.message ?: "Unknown Error"
e.printStackTrace()
}
}

val button = findViewById<Button>(R.id.button)
override fun close() {
channel.shutdownNow()
}
}

val nameText = findViewById<EditText>(R.id.name)
nameText.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
button.isEnabled = s.isNotEmpty()
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { }
override fun afterTextChanged(s: Editable) { }
})

val responseText = findViewById<TextView>(R.id.response)

fun sendReq() = runBlocking {
try {
val request = helloRequest { name = nameText.text.toString() }
val response = greeter.sayHello(request)
responseText.text = response.message
} catch (e: Exception) {
responseText.text = e.message
e.printStackTrace()
}
}
@Composable
fun Greeter(greeterRCP: GreeterRCP) {

button.setOnClickListener {
sendReq()
}
val scope = rememberCoroutineScope()

nameText.setOnKeyListener { _, keyCode, event ->
if ((event.action == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
sendReq()
true
} else {
false
}
val nameState = remember { mutableStateOf(TextFieldValue()) }

Column(Modifier.fillMaxWidth().fillMaxHeight(), Arrangement.Top, Alignment.CenterHorizontally) {
Text(stringResource(R.string.name_hint), modifier = Modifier.padding(top = 10.dp))
OutlinedTextField(nameState.value, { nameState.value = it })

Button({ scope.launch { greeterRCP.sayHello(nameState.value.text) } }, Modifier.padding(10.dp)) {
Text(stringResource(R.string.send_request))
}

if (greeterRCP.responseState.value.isNotEmpty()) {
Text(stringResource(R.string.server_response), modifier = Modifier.padding(top = 10.dp))
Text(greeterRCP.responseState.value)
}
}
}
36 changes: 0 additions & 36 deletions examples/android/src/main/res/layout/activity_main.xml

This file was deleted.

4 changes: 2 additions & 2 deletions examples/android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="name_hint">Name</string>
<string name="name_hint">Name:</string>
<string name="send_request">Send gRPC Request</string>
<string name="app_label">gRPC Kotlin Android</string>
<string name="server_response">Server response</string>
<string name="server_response">Server response: </string>
</resources>
8 changes: 4 additions & 4 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
plugins {
id("com.android.application") version "4.1.1" apply false
id("com.google.protobuf") version "0.8.17" apply false
kotlin("jvm") version "1.5.31" apply false
id("com.android.application") version "7.0.2" apply false
id("com.google.protobuf") version "0.8.18" apply false
kotlin("jvm") version "1.5.31" apply false // Compose Compiler required version
id("org.jlleitschuh.gradle.ktlint") version "10.2.0"
}

// todo: move to subprojects, but how?
ext["grpcVersion"] = "1.39.0" // need to wait for grpc kotlin to move past this
ext["grpcKotlinVersion"] = "1.2.0" // CURRENT_GRPC_KOTLIN_VERSION
ext["protobufVersion"] = "3.18.1"
ext["protobufVersion"] = "3.19.1"
ext["coroutinesVersion"] = "1.5.2"

allprojects {
Expand Down
5 changes: 5 additions & 0 deletions examples/client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ plugins {
kotlin("jvm")
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
implementation(project(":stub"))
runtimeOnly("io.grpc:grpc-netty:${rootProject.ext["grpcVersion"]}")
Expand Down
1 change: 1 addition & 0 deletions examples/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m
android.useAndroidX=true
2 changes: 1 addition & 1 deletion examples/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
5 changes: 5 additions & 0 deletions examples/native-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ plugins {
id("com.palantir.graal") version "0.7.2"
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
implementation(project(":stub-lite"))
runtimeOnly("io.grpc:grpc-okhttp:${rootProject.ext["grpcVersion"]}")
Expand Down
Loading